1 /* 2 * (MPSAFE) 3 * 4 * Copyright (c) 2006 The DragonFly Project. All rights reserved. 5 * 6 * This code is derived from software contributed to The DragonFly Project 7 * by Matthew Dillon <dillon@backplane.com> 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 3. Neither the name of The DragonFly Project nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific, prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 */ 37 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/conf.h> 41 #include <sys/cons.h> 42 #include <sys/tty.h> 43 #include <sys/fcntl.h> 44 #include <sys/signalvar.h> 45 #include <sys/eventhandler.h> 46 #include <sys/interrupt.h> 47 #include <sys/bus.h> 48 #include <machine/md_var.h> 49 #include <unistd.h> 50 #include <termios.h> 51 #include <stdlib.h> 52 53 static int console_stolen_by_kernel; 54 static struct kqueue_info *kqueue_console_info; 55 static struct tty *kqueue_console_tty; 56 static int vcons_started; 57 58 /************************************************************************ 59 * CONSOLE DEVICE * 60 ************************************************************************ 61 * 62 */ 63 64 static int vcons_tty_param(struct tty *tp, struct termios *tio); 65 static void vcons_tty_start(struct tty *tp); 66 static void vcons_hardintr(void *tpx, struct intrframe *frame __unused); 67 68 static d_open_t vcons_open; 69 static d_close_t vcons_close; 70 static d_ioctl_t vcons_ioctl; 71 72 static struct dev_ops vcons_ops = { 73 { "vcons", 0, D_TTY }, 74 .d_open = vcons_open, 75 .d_close = vcons_close, 76 .d_read = ttyread, 77 .d_write = ttywrite, 78 .d_ioctl = vcons_ioctl, 79 .d_kqfilter = ttykqfilter 80 }; 81 82 static int 83 vcons_open(struct dev_open_args *ap) 84 { 85 cdev_t dev = ap->a_head.a_dev; 86 struct tty *tp; 87 int error; 88 89 tp = ttymalloc(&dev->si_tty); 90 lwkt_gettoken(&tp->t_token); 91 92 #define ISSET(t, f) ((t) & (f)) 93 94 if ((tp->t_state & TS_ISOPEN) == 0) { 95 tp->t_oproc = vcons_tty_start; 96 tp->t_param = vcons_tty_param; 97 tp->t_stop = nottystop; 98 tp->t_dev = dev; 99 100 tp->t_state |= TS_CARR_ON | TS_CONNECTED; 101 ttychars(tp); 102 tp->t_iflag = TTYDEF_IFLAG; 103 tp->t_oflag = TTYDEF_OFLAG; 104 tp->t_cflag = TTYDEF_CFLAG; 105 tp->t_lflag = TTYDEF_LFLAG; 106 tp->t_ispeed = TTYDEF_SPEED; 107 tp->t_ospeed = TTYDEF_SPEED; 108 ttsetwater(tp); 109 } 110 if (minor(dev) == 0) { 111 error = (*linesw[tp->t_line].l_open)(dev, tp); 112 ioctl(0, TIOCGWINSZ, &tp->t_winsize); 113 114 if (kqueue_console_info == NULL) { 115 kqueue_console_tty = tp; 116 kqueue_console_info = kqueue_add(0, vcons_hardintr, tp); 117 } 118 } else { 119 /* dummy up other minors so the installer will run */ 120 error = 0; 121 } 122 lwkt_reltoken(&tp->t_token); 123 124 return(error); 125 } 126 127 static int 128 vcons_close(struct dev_close_args *ap) 129 { 130 cdev_t dev = ap->a_head.a_dev; 131 struct tty *tp; 132 133 tp = dev->si_tty; 134 lwkt_gettoken(&tp->t_token); 135 (*linesw[tp->t_line].l_close)(tp, ap->a_fflag); 136 ttyclose(tp); 137 lwkt_reltoken(&tp->t_token); 138 return(0); 139 } 140 141 static int 142 vcons_ioctl(struct dev_ioctl_args *ap) 143 { 144 cdev_t dev = ap->a_head.a_dev; 145 struct tty *tp; 146 int error; 147 148 tp = dev->si_tty; 149 lwkt_gettoken(&tp->t_token); 150 error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data, 151 ap->a_fflag, ap->a_cred); 152 if (error != ENOIOCTL) { 153 lwkt_reltoken(&tp->t_token); 154 return (error); 155 } 156 error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag); 157 if (error != ENOIOCTL) { 158 lwkt_reltoken(&tp->t_token); 159 return (error); 160 } 161 lwkt_reltoken(&tp->t_token); 162 163 return (ENOTTY); 164 } 165 166 static int 167 vcons_tty_param(struct tty *tp, struct termios *tio) 168 { 169 lwkt_gettoken(&tp->t_token); 170 tp->t_ispeed = tio->c_ispeed; 171 tp->t_ospeed = tio->c_ospeed; 172 tp->t_cflag = tio->c_cflag; 173 lwkt_reltoken(&tp->t_token); 174 return(0); 175 } 176 177 static void 178 vcons_tty_start(struct tty *tp) 179 { 180 int n; 181 char buf[64]; 182 183 lwkt_gettoken(&tp->t_token); 184 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 185 ttwwakeup(tp); 186 lwkt_reltoken(&tp->t_token); 187 return; 188 } 189 tp->t_state |= TS_BUSY; 190 while ((n = clist_qtob(&tp->t_outq, buf, sizeof(buf))) > 0) { 191 /* 192 * Dummy up ttyv1, etc. 193 */ 194 if (minor(tp->t_dev) == 0) { 195 pwrite(1, buf, n, -1); 196 } 197 } 198 tp->t_state &= ~TS_BUSY; 199 lwkt_reltoken(&tp->t_token); 200 ttwwakeup(tp); 201 } 202 203 static 204 void 205 vcons_hardintr(void *tpx, struct intrframe *frame __unused) 206 { 207 if (console_stolen_by_kernel == 0) 208 signalintr(4); 209 } 210 211 /************************************************************************ 212 * KERNEL CONSOLE INTERFACE * 213 ************************************************************************ 214 * 215 * Kernel direct-call interface console driver 216 */ 217 static cn_probe_t vconsprobe; 218 static cn_init_t vconsinit; 219 static cn_init_fini_t vconsinit_fini; 220 static cn_term_t vconsterm; 221 static cn_getc_t vconsgetc; 222 static cn_checkc_t vconscheckc; 223 static cn_putc_t vconsputc; 224 225 CONS_DRIVER(vcons, vconsprobe, vconsinit, vconsinit_fini, vconsterm, vconsgetc, 226 vconscheckc, vconsputc, NULL, NULL); 227 228 static struct termios init_tio; 229 static struct consdev *vconsole; 230 231 static void 232 vconsprobe(struct consdev *cp) 233 { 234 cp->cn_pri = CN_NORMAL; 235 cp->cn_probegood = 1; 236 } 237 238 /* 239 * This is a little bulky handler to set proper terminal 240 * settings in the case of a signal which might lead to 241 * termination or suspension. 242 */ 243 static void 244 vconssignal(int sig) 245 { 246 struct termios curtio; 247 struct sigaction sa, osa; 248 sigset_t ss, oss; 249 250 tcgetattr(0, &curtio); 251 tcsetattr(0, TCSAFLUSH, &init_tio); 252 bzero(&sa, sizeof(sa)); 253 sigemptyset(&sa.sa_mask); 254 sa.sa_handler = SIG_DFL; 255 sigaction(sig, &sa, &osa); 256 sigemptyset(&ss); 257 sigaddset(&ss, sig); 258 sigprocmask(SIG_UNBLOCK, &ss, &oss); 259 raise(sig); /* now hand down the sig */ 260 sigprocmask(SIG_SETMASK, &oss, NULL); 261 sigaction(sig, &osa, NULL); 262 tcsetattr(0, TCSAFLUSH, &curtio); 263 } 264 265 static void 266 vconswinchsig(int __unused sig) 267 { 268 signalintr(3); 269 } 270 271 static void 272 vconswinch_intr(void *arg __unused, void *frame __unused) 273 { 274 struct winsize newsize; 275 276 if (vconsole != NULL && vconsole->cn_dev->si_tty != NULL) { 277 ioctl(0, TIOCGWINSZ, &newsize); 278 /* 279 * ttioctl(vconsole->cn_dev->si_tty, TIOCSWINSZ, &newsize, 0); 280 * I wished. Unfortunately this needs a curproc, so do it 281 * manually. 282 */ 283 if (bcmp(&newsize, &vconsole->cn_dev->si_tty->t_winsize, 284 sizeof(newsize)) != 0) { 285 vconsole->cn_dev->si_tty->t_winsize = newsize; 286 pgsignal(vconsole->cn_dev->si_tty->t_pgrp, SIGWINCH, 1); 287 } 288 } 289 } 290 291 /* 292 * This has to be an interrupt thread and not a hard interrupt. 293 */ 294 static 295 void 296 vconsvirt_intr(void *arg __unused, void *frame __unused) 297 { 298 struct tty *tp; 299 unsigned char buf[32]; 300 int i; 301 int n; 302 303 if (kqueue_console_info == NULL) 304 return; 305 tp = kqueue_console_tty; 306 307 lwkt_gettoken(&tp->t_token); 308 /* 309 * If we aren't open we only have synchronous traffic via the 310 * debugger and do not need to poll. 311 */ 312 if ((tp->t_state & TS_ISOPEN) == 0) { 313 lwkt_reltoken(&tp->t_token); 314 return; 315 } 316 317 /* 318 * Only poll if we are open and haven't been stolen by the debugger. 319 */ 320 if (console_stolen_by_kernel == 0 && (tp->t_state & TS_ISOPEN)) { 321 do { 322 n = extpread(0, buf, sizeof(buf), O_FNONBLOCKING, -1LL); 323 for (i = 0; i < n; ++i) 324 (*linesw[tp->t_line].l_rint)(buf[i], tp); 325 } while (n > 0); 326 } 327 lwkt_reltoken(&tp->t_token); 328 } 329 330 331 332 static void 333 vconscleanup(void) 334 { 335 /* 336 * We might catch stray SIGIOs, so try hard. 337 */ 338 while (tcsetattr(0, TCSAFLUSH, &init_tio) != 0 && errno == EINTR) 339 /* NOTHING */; 340 } 341 342 static void 343 vconsinit(struct consdev *cp) 344 { 345 struct sigaction sa; 346 347 if (vcons_started) 348 return; 349 vcons_started = 1; 350 vconsole = cp; 351 352 tcgetattr(0, &init_tio); 353 bzero(&sa, sizeof(sa)); 354 sigemptyset(&sa.sa_mask); 355 sa.sa_handler = vconssignal; 356 sigaction(SIGTSTP, &sa, NULL); 357 sigaction(SIGINT, &sa, NULL); 358 sigaction(SIGTERM, &sa, NULL); 359 atexit(vconscleanup); 360 vcons_set_mode(0); 361 } 362 363 static void 364 vconsinit_fini(struct consdev *cp) 365 { 366 struct sigaction sa; 367 cdev_t dev; 368 int i; 369 370 /* 371 * We have to do this here rather then in early boot to be able 372 * to use the interrupt subsystem. 373 */ 374 register_int_virtual(3, vconswinch_intr, NULL, "swinch", NULL, 375 INTR_MPSAFE); 376 register_int_virtual(4, vconsvirt_intr, NULL, "vintr", NULL, 377 INTR_MPSAFE); 378 bzero(&sa, sizeof(sa)); 379 sigemptyset(&sa.sa_mask); 380 sa.sa_handler = vconswinchsig; 381 sigaction(SIGWINCH, &sa, NULL); 382 383 /* 384 * Implement ttyv0-ttyv7. At the moment ttyv1-7 are sink nulls. 385 */ 386 for (i = 0; i < 8; ++i) { 387 dev = make_dev(&vcons_ops, i, 388 UID_ROOT, GID_WHEEL, 0600, "ttyv%d", i); 389 if (i == 0) { 390 cp->cn_dev = dev; 391 } 392 } 393 EVENTHANDLER_REGISTER(shutdown_final, vconscleanup, NULL, SHUTDOWN_PRI_LAST); 394 } 395 396 static void 397 vconsterm(struct consdev *vp) 398 { 399 vconsole = NULL; 400 vconscleanup(); 401 } 402 403 static int 404 vconsgetc(void *private) 405 { 406 unsigned char c; 407 ssize_t n; 408 409 console_stolen_by_kernel = 1; 410 for (;;) { 411 n = pread(0, &c, 1, -1); 412 if (n == 1) 413 break; 414 if (n < 0 && errno == EINTR) 415 continue; 416 panic("vconsgetc: EOF on console %jd %d", (intmax_t)n, errno); 417 } 418 console_stolen_by_kernel = 0; 419 return((int)c); 420 } 421 422 static int 423 vconscheckc(void *private) 424 { 425 unsigned char c; 426 427 if (extpread(0, &c, 1, O_FNONBLOCKING, -1LL) == 1) 428 return((int)c); 429 return(-1); 430 } 431 432 static void 433 vconsputc(void *private, int c) 434 { 435 char cc = c; 436 437 pwrite(1, &cc, 1, -1); 438 } 439 440 void 441 vcons_set_mode(int in_debugger) 442 { 443 struct termios tio; 444 445 if (tcgetattr(0, &tio) < 0) { 446 return; 447 } 448 cfmakeraw(&tio); 449 tio.c_oflag |= OPOST | ONLCR; 450 tio.c_lflag |= ISIG; 451 if (in_debugger) { 452 tio.c_cc[VINTR] = init_tio.c_cc[VINTR]; 453 tio.c_cc[VSUSP] = init_tio.c_cc[VSUSP]; 454 tio.c_cc[VSTATUS] = init_tio.c_cc[VSTATUS]; 455 } else { 456 tio.c_cc[VINTR] = _POSIX_VDISABLE; 457 tio.c_cc[VSUSP] = _POSIX_VDISABLE; 458 tio.c_cc[VSTATUS] = _POSIX_VDISABLE; 459 } 460 tcsetattr(0, TCSAFLUSH, &tio); 461 } 462