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