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 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 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 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 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 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 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 130 void sigio_broken(void) 131 { 132 write_sigio_workaround(); 133 } 134 135 /* Changed during early boot */ 136 static int pty_output_sigio; 137 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 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 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 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 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 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 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 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 */ 301 void __init os_check_bugs(void) 302 { 303 check_sigio(); 304 } 305