1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
4 */
5
6 #include <unistd.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <poll.h>
10 #include <pty.h>
11 #include <sched.h>
12 #include <signal.h>
13 #include <string.h>
14 #include <sys/epoll.h>
15 #include <asm/unistd.h>
16 #include <kern_util.h>
17 #include <init.h>
18 #include <os.h>
19 #include <sigio.h>
20 #include <um_malloc.h>
21
22 /*
23 * Protected by sigio_lock(), also used by sigio_cleanup, which is an
24 * exitcall.
25 */
26 static struct os_helper_thread *write_sigio_td;
27
28 static int epollfd = -1;
29
30 #define MAX_EPOLL_EVENTS 64
31
32 static struct epoll_event epoll_events[MAX_EPOLL_EVENTS];
33
write_sigio_thread(void * unused)34 static void *write_sigio_thread(void *unused)
35 {
36 int pid = getpid();
37 int r;
38
39 os_fix_helper_thread_signals();
40
41 while (1) {
42 r = epoll_wait(epollfd, epoll_events, MAX_EPOLL_EVENTS, -1);
43 if (r < 0) {
44 if (errno == EINTR)
45 continue;
46 printk(UM_KERN_ERR "%s: epoll_wait failed, errno = %d\n",
47 __func__, errno);
48 }
49
50 CATCH_EINTR(r = syscall(__NR_tgkill, pid, pid, SIGIO));
51 if (r < 0)
52 printk(UM_KERN_ERR "%s: tgkill failed, errno = %d\n",
53 __func__, errno);
54 }
55
56 return NULL;
57 }
58
__add_sigio_fd(int fd)59 int __add_sigio_fd(int fd)
60 {
61 struct epoll_event event = {
62 .data.fd = fd,
63 .events = EPOLLIN | EPOLLET,
64 };
65 int r;
66
67 CATCH_EINTR(r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event));
68 return r < 0 ? -errno : 0;
69 }
70
add_sigio_fd(int fd)71 int add_sigio_fd(int fd)
72 {
73 int err;
74
75 sigio_lock();
76 err = __add_sigio_fd(fd);
77 sigio_unlock();
78
79 return err;
80 }
81
__ignore_sigio_fd(int fd)82 int __ignore_sigio_fd(int fd)
83 {
84 struct epoll_event event;
85 int r;
86
87 CATCH_EINTR(r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event));
88 return r < 0 ? -errno : 0;
89 }
90
ignore_sigio_fd(int fd)91 int ignore_sigio_fd(int fd)
92 {
93 int err;
94
95 sigio_lock();
96 err = __ignore_sigio_fd(fd);
97 sigio_unlock();
98
99 return err;
100 }
101
write_sigio_workaround(void)102 static void write_sigio_workaround(void)
103 {
104 int err;
105
106 sigio_lock();
107 if (write_sigio_td)
108 goto out;
109
110 epollfd = epoll_create(MAX_EPOLL_EVENTS);
111 if (epollfd < 0) {
112 printk(UM_KERN_ERR "%s: epoll_create failed, errno = %d\n",
113 __func__, errno);
114 goto out;
115 }
116
117 err = os_run_helper_thread(&write_sigio_td, write_sigio_thread, NULL);
118 if (err < 0) {
119 printk(UM_KERN_ERR "%s: os_run_helper_thread failed, errno = %d\n",
120 __func__, -err);
121 close(epollfd);
122 epollfd = -1;
123 goto out;
124 }
125
126 out:
127 sigio_unlock();
128 }
129
sigio_broken(void)130 void sigio_broken(void)
131 {
132 write_sigio_workaround();
133 }
134
135 /* Changed during early boot */
136 static int pty_output_sigio;
137
maybe_sigio_broken(int fd)138 void maybe_sigio_broken(int fd)
139 {
140 if (!isatty(fd))
141 return;
142
143 if (pty_output_sigio)
144 return;
145
146 sigio_broken();
147 }
148
sigio_cleanup(void)149 static void sigio_cleanup(void)
150 {
151 if (!write_sigio_td)
152 return;
153
154 os_kill_helper_thread(write_sigio_td);
155 write_sigio_td = NULL;
156 }
157
158 __uml_exitcall(sigio_cleanup);
159
160 /* Used as a flag during SIGIO testing early in boot */
161 static int got_sigio;
162
handler(int sig)163 static void __init handler(int sig)
164 {
165 got_sigio = 1;
166 }
167
168 struct openpty_arg {
169 int master;
170 int slave;
171 int err;
172 };
173
openpty_cb(void * arg)174 static void openpty_cb(void *arg)
175 {
176 struct openpty_arg *info = arg;
177
178 info->err = 0;
179 if (openpty(&info->master, &info->slave, NULL, NULL, NULL))
180 info->err = -errno;
181 }
182
async_pty(int master,int slave)183 static int async_pty(int master, int slave)
184 {
185 int flags;
186
187 flags = fcntl(master, F_GETFL);
188 if (flags < 0)
189 return -errno;
190
191 if ((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) ||
192 (fcntl(master, F_SETOWN, os_getpid()) < 0))
193 return -errno;
194
195 if ((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0))
196 return -errno;
197
198 return 0;
199 }
200
check_one_sigio(void (* proc)(int,int))201 static void __init check_one_sigio(void (*proc)(int, int))
202 {
203 struct sigaction old, new;
204 struct openpty_arg pty = { .master = -1, .slave = -1 };
205 int master, slave, err;
206
207 initial_thread_cb(openpty_cb, &pty);
208 if (pty.err) {
209 printk(UM_KERN_ERR "check_one_sigio failed, errno = %d\n",
210 -pty.err);
211 return;
212 }
213
214 master = pty.master;
215 slave = pty.slave;
216
217 if ((master == -1) || (slave == -1)) {
218 printk(UM_KERN_ERR "check_one_sigio failed to allocate a "
219 "pty\n");
220 return;
221 }
222
223 /* Not now, but complain so we now where we failed. */
224 err = raw(master);
225 if (err < 0) {
226 printk(UM_KERN_ERR "check_one_sigio : raw failed, errno = %d\n",
227 -err);
228 return;
229 }
230
231 err = async_pty(master, slave);
232 if (err < 0) {
233 printk(UM_KERN_ERR "check_one_sigio : sigio_async failed, "
234 "err = %d\n", -err);
235 return;
236 }
237
238 if (sigaction(SIGIO, NULL, &old) < 0) {
239 printk(UM_KERN_ERR "check_one_sigio : sigaction 1 failed, "
240 "errno = %d\n", errno);
241 return;
242 }
243
244 new = old;
245 new.sa_handler = handler;
246 if (sigaction(SIGIO, &new, NULL) < 0) {
247 printk(UM_KERN_ERR "check_one_sigio : sigaction 2 failed, "
248 "errno = %d\n", errno);
249 return;
250 }
251
252 got_sigio = 0;
253 (*proc)(master, slave);
254
255 close(master);
256 close(slave);
257
258 if (sigaction(SIGIO, &old, NULL) < 0)
259 printk(UM_KERN_ERR "check_one_sigio : sigaction 3 failed, "
260 "errno = %d\n", errno);
261 }
262
tty_output(int master,int slave)263 static void tty_output(int master, int slave)
264 {
265 int n;
266 char buf[512];
267
268 printk(UM_KERN_INFO "Checking that host ptys support output SIGIO...");
269
270 memset(buf, 0, sizeof(buf));
271
272 while (write(master, buf, sizeof(buf)) > 0) ;
273 if (errno != EAGAIN)
274 printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n",
275 errno);
276 while (((n = read(slave, buf, sizeof(buf))) > 0) &&
277 !({ barrier(); got_sigio; }))
278 ;
279
280 if (got_sigio) {
281 printk(UM_KERN_CONT "Yes\n");
282 pty_output_sigio = 1;
283 } else if (n == -EAGAIN)
284 printk(UM_KERN_CONT "No, enabling workaround\n");
285 else
286 printk(UM_KERN_CONT "tty_output : read failed, err = %d\n", n);
287 }
288
check_sigio(void)289 static void __init check_sigio(void)
290 {
291 if ((access("/dev/ptmx", R_OK) < 0) &&
292 (access("/dev/ptyp0", R_OK) < 0)) {
293 printk(UM_KERN_WARNING "No pseudo-terminals available - "
294 "skipping pty SIGIO check\n");
295 return;
296 }
297 check_one_sigio(tty_output);
298 }
299
300 /* Here because it only does the SIGIO testing for now */
os_check_bugs(void)301 void __init os_check_bugs(void)
302 {
303 check_sigio();
304 }
305