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