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 lwkt_gettoken(&tty_token); 90 tp = dev->si_tty = ttymalloc(dev->si_tty); 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(&tty_token); 123 return(error); 124 } 125 126 static int 127 vcons_close(struct dev_close_args *ap) 128 { 129 cdev_t dev = ap->a_head.a_dev; 130 struct tty *tp; 131 132 lwkt_gettoken(&tty_token); 133 tp = dev->si_tty; 134 (*linesw[tp->t_line].l_close)(tp, ap->a_fflag); 135 ttyclose(tp); 136 lwkt_reltoken(&tty_token); 137 return(0); 138 } 139 140 static int 141 vcons_ioctl(struct dev_ioctl_args *ap) 142 { 143 cdev_t dev = ap->a_head.a_dev; 144 struct tty *tp; 145 int error; 146 147 lwkt_gettoken(&tty_token); 148 tp = dev->si_tty; 149 error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data, 150 ap->a_fflag, ap->a_cred); 151 if (error != ENOIOCTL) { 152 lwkt_reltoken(&tty_token); 153 return (error); 154 } 155 error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag); 156 if (error != ENOIOCTL) { 157 lwkt_reltoken(&tty_token); 158 return (error); 159 } 160 lwkt_reltoken(&tty_token); 161 return (ENOTTY); 162 } 163 164 static int 165 vcons_tty_param(struct tty *tp, struct termios *tio) 166 { 167 lwkt_gettoken(&tty_token); 168 tp->t_ispeed = tio->c_ispeed; 169 tp->t_ospeed = tio->c_ospeed; 170 tp->t_cflag = tio->c_cflag; 171 lwkt_reltoken(&tty_token); 172 return(0); 173 } 174 175 static void 176 vcons_tty_start(struct tty *tp) 177 { 178 int n; 179 char buf[64]; 180 181 lwkt_gettoken(&tty_token); 182 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 183 ttwwakeup(tp); 184 lwkt_reltoken(&tty_token); 185 return; 186 } 187 tp->t_state |= TS_BUSY; 188 while ((n = q_to_b(&tp->t_outq, buf, sizeof(buf))) > 0) { 189 /* 190 * Dummy up ttyv1, etc. 191 */ 192 if (minor(tp->t_dev) == 0) { 193 pwrite(1, buf, n, -1); 194 } 195 } 196 tp->t_state &= ~TS_BUSY; 197 lwkt_reltoken(&tty_token); 198 ttwwakeup(tp); 199 } 200 201 static 202 void 203 vcons_hardintr(void *tpx, struct intrframe *frame __unused) 204 { 205 if (console_stolen_by_kernel == 0) 206 signalintr(4); 207 } 208 209 /************************************************************************ 210 * KERNEL CONSOLE INTERFACE * 211 ************************************************************************ 212 * 213 * Kernel direct-call interface console driver 214 */ 215 static cn_probe_t vconsprobe; 216 static cn_init_t vconsinit; 217 static cn_init_fini_t vconsinit_fini; 218 static cn_term_t vconsterm; 219 static cn_getc_t vconsgetc; 220 static cn_checkc_t vconscheckc; 221 static cn_putc_t vconsputc; 222 223 CONS_DRIVER(vcons, vconsprobe, vconsinit, vconsinit_fini, vconsterm, vconsgetc, 224 vconscheckc, vconsputc, NULL, NULL); 225 226 static struct termios init_tio; 227 static struct consdev *vconsole; 228 229 static void 230 vconsprobe(struct consdev *cp) 231 { 232 cp->cn_pri = CN_NORMAL; 233 cp->cn_probegood = 1; 234 } 235 236 /* 237 * This is a little bulky handler to set proper terminal 238 * settings in the case of a signal which might lead to 239 * termination or suspension. 240 */ 241 static void 242 vconssignal(int sig) 243 { 244 struct termios curtio; 245 struct sigaction sa, osa; 246 sigset_t ss, oss; 247 248 tcgetattr(0, &curtio); 249 tcsetattr(0, TCSAFLUSH, &init_tio); 250 bzero(&sa, sizeof(sa)); 251 sigemptyset(&sa.sa_mask); 252 sa.sa_handler = SIG_DFL; 253 sigaction(sig, &sa, &osa); 254 sigemptyset(&ss); 255 sigaddset(&ss, sig); 256 sigprocmask(SIG_UNBLOCK, &ss, &oss); 257 raise(sig); /* now hand down the sig */ 258 sigprocmask(SIG_SETMASK, &oss, NULL); 259 sigaction(sig, &osa, NULL); 260 tcsetattr(0, TCSAFLUSH, &curtio); 261 } 262 263 static void 264 vconswinchsig(int __unused sig) 265 { 266 signalintr(3); 267 } 268 269 static void 270 vconswinch_intr(void *arg __unused, void *frame __unused) 271 { 272 struct winsize newsize; 273 274 if (vconsole != NULL && vconsole->cn_dev->si_tty != NULL) { 275 ioctl(0, TIOCGWINSZ, &newsize); 276 /* 277 * ttioctl(vconsole->cn_dev->si_tty, TIOCSWINSZ, &newsize, 0); 278 * I wished. Unfortunately this needs a curproc, so do it 279 * manually. 280 */ 281 if (bcmp(&newsize, &vconsole->cn_dev->si_tty->t_winsize, 282 sizeof(newsize)) != 0) { 283 vconsole->cn_dev->si_tty->t_winsize = newsize; 284 pgsignal(vconsole->cn_dev->si_tty->t_pgrp, SIGWINCH, 1); 285 } 286 } 287 } 288 289 /* 290 * This has to be an interrupt thread and not a hard interrupt. 291 */ 292 static 293 void 294 vconsvirt_intr(void *arg __unused, void *frame __unused) 295 { 296 struct tty *tp; 297 unsigned char buf[32]; 298 int i; 299 int n; 300 301 if (kqueue_console_info == NULL) 302 return; 303 tp = kqueue_console_tty; 304 305 lwkt_gettoken(&tty_token); 306 /* 307 * If we aren't open we only have synchronous traffic via the 308 * debugger and do not need to poll. 309 */ 310 if ((tp->t_state & TS_ISOPEN) == 0) { 311 lwkt_reltoken(&tty_token); 312 return; 313 } 314 315 /* 316 * Only poll if we are open and haven't been stolen by the debugger. 317 */ 318 if (console_stolen_by_kernel == 0 && (tp->t_state & TS_ISOPEN)) { 319 do { 320 n = extpread(0, buf, sizeof(buf), O_FNONBLOCKING, -1LL); 321 for (i = 0; i < n; ++i) 322 (*linesw[tp->t_line].l_rint)(buf[i], tp); 323 } while (n > 0); 324 } 325 lwkt_reltoken(&tty_token); 326 } 327 328 329 330 static void 331 vconscleanup(void) 332 { 333 /* 334 * We might catch stray SIGIOs, so try hard. 335 */ 336 while (tcsetattr(0, TCSAFLUSH, &init_tio) != 0 && errno == EINTR) 337 /* NOTHING */; 338 } 339 340 static void 341 vconsinit(struct consdev *cp) 342 { 343 struct sigaction sa; 344 345 if (vcons_started) 346 return; 347 vcons_started = 1; 348 vconsole = cp; 349 350 tcgetattr(0, &init_tio); 351 bzero(&sa, sizeof(sa)); 352 sigemptyset(&sa.sa_mask); 353 sa.sa_handler = vconssignal; 354 sigaction(SIGTSTP, &sa, NULL); 355 sigaction(SIGINT, &sa, NULL); 356 sigaction(SIGTERM, &sa, NULL); 357 atexit(vconscleanup); 358 vcons_set_mode(0); 359 } 360 361 static void 362 vconsinit_fini(struct consdev *cp) 363 { 364 struct sigaction sa; 365 cdev_t dev; 366 int i; 367 368 /* 369 * We have to do this here rather then in early boot to be able 370 * to use the interrupt subsystem. 371 */ 372 register_int_virtual(3, vconswinch_intr, NULL, "swinch", NULL, 373 INTR_MPSAFE); 374 register_int_virtual(4, vconsvirt_intr, NULL, "vintr", NULL, 375 INTR_MPSAFE); 376 bzero(&sa, sizeof(sa)); 377 sigemptyset(&sa.sa_mask); 378 sa.sa_handler = vconswinchsig; 379 sigaction(SIGWINCH, &sa, NULL); 380 381 /* 382 * Implement ttyv0-ttyv7. At the moment ttyv1-7 are sink nulls. 383 */ 384 for (i = 0; i < 8; ++i) { 385 dev = make_dev(&vcons_ops, i, 386 UID_ROOT, GID_WHEEL, 0600, "ttyv%d", i); 387 if (i == 0) { 388 cp->cn_dev = dev; 389 } 390 } 391 EVENTHANDLER_REGISTER(shutdown_final, vconscleanup, NULL, SHUTDOWN_PRI_LAST); 392 } 393 394 static void 395 vconsterm(struct consdev *vp) 396 { 397 vconsole = NULL; 398 vconscleanup(); 399 } 400 401 static int 402 vconsgetc(void *private) 403 { 404 unsigned char c; 405 ssize_t n; 406 407 console_stolen_by_kernel = 1; 408 for (;;) { 409 n = pread(0, &c, 1, -1); 410 if (n == 1) 411 break; 412 if (n < 0 && errno == EINTR) 413 continue; 414 panic("vconsgetc: EOF on console %jd %d", (intmax_t)n, errno); 415 } 416 console_stolen_by_kernel = 0; 417 return((int)c); 418 } 419 420 static int 421 vconscheckc(void *private) 422 { 423 unsigned char c; 424 425 if (extpread(0, &c, 1, O_FNONBLOCKING, -1LL) == 1) 426 return((int)c); 427 return(-1); 428 } 429 430 static void 431 vconsputc(void *private, int c) 432 { 433 char cc = c; 434 435 pwrite(1, &cc, 1, -1); 436 } 437 438 void 439 vcons_set_mode(int in_debugger) 440 { 441 struct termios tio; 442 443 if (tcgetattr(0, &tio) < 0) { 444 return; 445 } 446 cfmakeraw(&tio); 447 tio.c_oflag |= OPOST | ONLCR; 448 tio.c_lflag |= ISIG; 449 if (in_debugger) { 450 tio.c_cc[VINTR] = init_tio.c_cc[VINTR]; 451 tio.c_cc[VSUSP] = init_tio.c_cc[VSUSP]; 452 tio.c_cc[VSTATUS] = init_tio.c_cc[VSTATUS]; 453 } else { 454 tio.c_cc[VINTR] = _POSIX_VDISABLE; 455 tio.c_cc[VSUSP] = _POSIX_VDISABLE; 456 tio.c_cc[VSTATUS] = _POSIX_VDISABLE; 457 } 458 tcsetattr(0, TCSAFLUSH, &tio); 459 } 460