1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
4 | */ |
5 | |
6 | #include <stdarg.h> |
7 | #include <stdio.h> |
8 | #include <stdlib.h> |
9 | #include <unistd.h> |
10 | #include <errno.h> |
11 | #include <signal.h> |
12 | #include <string.h> |
13 | #include <termios.h> |
14 | #include <sys/wait.h> |
15 | #include <sys/mman.h> |
16 | #include <sys/utsname.h> |
17 | #include <sys/random.h> |
18 | #include <init.h> |
19 | #include <os.h> |
20 | |
21 | void stack_protections(unsigned long address) |
22 | { |
23 | if (mprotect((void *) address, UM_THREAD_SIZE, |
24 | PROT_READ | PROT_WRITE | PROT_EXEC) < 0) |
25 | panic("protecting stack failed, errno = %d" , errno); |
26 | } |
27 | |
28 | int raw(int fd) |
29 | { |
30 | struct termios tt; |
31 | int err; |
32 | |
33 | CATCH_EINTR(err = tcgetattr(fd, &tt)); |
34 | if (err < 0) |
35 | return -errno; |
36 | |
37 | cfmakeraw(&tt); |
38 | |
39 | CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt)); |
40 | if (err < 0) |
41 | return -errno; |
42 | |
43 | /* |
44 | * XXX tcsetattr could have applied only some changes |
45 | * (and cfmakeraw() is a set of changes) |
46 | */ |
47 | return 0; |
48 | } |
49 | |
50 | void setup_machinename(char *machine_out) |
51 | { |
52 | struct utsname host; |
53 | |
54 | uname(&host); |
55 | #ifdef UML_CONFIG_UML_X86 |
56 | # ifndef UML_CONFIG_64BIT |
57 | if (!strcmp(host.machine, "x86_64" )) { |
58 | strcpy(machine_out, "i686" ); |
59 | return; |
60 | } |
61 | # else |
62 | if (!strcmp(host.machine, "i686" )) { |
63 | strcpy(machine_out, "x86_64" ); |
64 | return; |
65 | } |
66 | # endif |
67 | #endif |
68 | strcpy(machine_out, host.machine); |
69 | } |
70 | |
71 | void setup_hostinfo(char *buf, int len) |
72 | { |
73 | struct utsname host; |
74 | |
75 | uname(&host); |
76 | snprintf(buf, len, "%s %s %s %s %s" , host.sysname, host.nodename, |
77 | host.release, host.version, host.machine); |
78 | } |
79 | |
80 | /* |
81 | * We cannot use glibc's abort(). It makes use of tgkill() which |
82 | * has no effect within UML's kernel threads. |
83 | * After that glibc would execute an invalid instruction to kill |
84 | * the calling process and UML crashes with SIGSEGV. |
85 | */ |
86 | static inline void __attribute__ ((noreturn)) uml_abort(void) |
87 | { |
88 | sigset_t sig; |
89 | |
90 | fflush(NULL); |
91 | |
92 | if (!sigemptyset(&sig) && !sigaddset(&sig, SIGABRT)) |
93 | sigprocmask(SIG_UNBLOCK, &sig, 0); |
94 | |
95 | for (;;) |
96 | if (kill(getpid(), SIGABRT) < 0) |
97 | exit(127); |
98 | } |
99 | |
100 | ssize_t os_getrandom(void *buf, size_t len, unsigned int flags) |
101 | { |
102 | return getrandom(buf, len, flags); |
103 | } |
104 | |
105 | /* |
106 | * UML helper threads must not handle SIGWINCH/INT/TERM |
107 | */ |
108 | void os_fix_helper_signals(void) |
109 | { |
110 | signal(SIGWINCH, SIG_IGN); |
111 | signal(SIGINT, SIG_DFL); |
112 | signal(SIGTERM, SIG_DFL); |
113 | } |
114 | |
115 | void os_dump_core(void) |
116 | { |
117 | int pid; |
118 | |
119 | signal(SIGSEGV, SIG_DFL); |
120 | |
121 | /* |
122 | * We are about to SIGTERM this entire process group to ensure that |
123 | * nothing is around to run after the kernel exits. The |
124 | * kernel wants to abort, not die through SIGTERM, so we |
125 | * ignore it here. |
126 | */ |
127 | |
128 | signal(SIGTERM, SIG_IGN); |
129 | kill(0, SIGTERM); |
130 | /* |
131 | * Most of the other processes associated with this UML are |
132 | * likely sTopped, so give them a SIGCONT so they see the |
133 | * SIGTERM. |
134 | */ |
135 | kill(0, SIGCONT); |
136 | |
137 | /* |
138 | * Now, having sent signals to everyone but us, make sure they |
139 | * die by ptrace. Processes can survive what's been done to |
140 | * them so far - the mechanism I understand is receiving a |
141 | * SIGSEGV and segfaulting immediately upon return. There is |
142 | * always a SIGSEGV pending, and (I'm guessing) signals are |
143 | * processed in numeric order so the SIGTERM (signal 15 vs |
144 | * SIGSEGV being signal 11) is never handled. |
145 | * |
146 | * Run a waitpid loop until we get some kind of error. |
147 | * Hopefully, it's ECHILD, but there's not a lot we can do if |
148 | * it's something else. Tell os_kill_ptraced_process not to |
149 | * wait for the child to report its death because there's |
150 | * nothing reasonable to do if that fails. |
151 | */ |
152 | |
153 | while ((pid = waitpid(-1, NULL, WNOHANG | __WALL)) > 0) |
154 | os_kill_ptraced_process(pid, 0); |
155 | |
156 | uml_abort(); |
157 | } |
158 | |
159 | void um_early_printk(const char *s, unsigned int n) |
160 | { |
161 | printf("%.*s" , n, s); |
162 | } |
163 | |
164 | static int quiet_info; |
165 | |
166 | static int __init quiet_cmd_param(char *str, int *add) |
167 | { |
168 | quiet_info = 1; |
169 | return 0; |
170 | } |
171 | |
172 | __uml_setup("quiet" , quiet_cmd_param, |
173 | "quiet\n" |
174 | " Turns off information messages during boot.\n\n" ); |
175 | |
176 | /* |
177 | * The os_info/os_warn functions will be called by helper threads. These |
178 | * have a very limited stack size and using the libc formatting functions |
179 | * may overflow the stack. |
180 | * So pull in the kernel vscnprintf and use that instead with a fixed |
181 | * on-stack buffer. |
182 | */ |
183 | int vscnprintf(char *buf, size_t size, const char *fmt, va_list args); |
184 | |
185 | void os_info(const char *fmt, ...) |
186 | { |
187 | char buf[256]; |
188 | va_list list; |
189 | int len; |
190 | |
191 | if (quiet_info) |
192 | return; |
193 | |
194 | va_start(list, fmt); |
195 | len = vscnprintf(buf, sizeof(buf), fmt, list); |
196 | fwrite(buf, len, 1, stderr); |
197 | va_end(list); |
198 | } |
199 | |
200 | void os_warn(const char *fmt, ...) |
201 | { |
202 | char buf[256]; |
203 | va_list list; |
204 | int len; |
205 | |
206 | va_start(list, fmt); |
207 | len = vscnprintf(buf, sizeof(buf), fmt, list); |
208 | fwrite(buf, len, 1, stderr); |
209 | va_end(list); |
210 | } |
211 | |