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 56 /************************************************************************ 57 * CONSOLE DEVICE * 58 ************************************************************************ 59 * 60 */ 61 62 static int vcons_tty_param(struct tty *tp, struct termios *tio); 63 static void vcons_tty_start(struct tty *tp); 64 static void vcons_intr(void *tpx, struct intrframe *frame __unused); 65 66 static d_open_t vcons_open; 67 static d_close_t vcons_close; 68 static d_ioctl_t vcons_ioctl; 69 70 static struct dev_ops vcons_ops = { 71 { "vcons", 0, D_TTY }, 72 .d_open = vcons_open, 73 .d_close = vcons_close, 74 .d_read = ttyread, 75 .d_write = ttywrite, 76 .d_ioctl = vcons_ioctl, 77 .d_kqfilter = ttykqfilter 78 }; 79 80 static int 81 vcons_open(struct dev_open_args *ap) 82 { 83 cdev_t dev = ap->a_head.a_dev; 84 struct tty *tp; 85 int error; 86 87 lwkt_gettoken(&tty_token); 88 tp = dev->si_tty = ttymalloc(dev->si_tty); 89 90 #define ISSET(t, f) ((t) & (f)) 91 92 if ((tp->t_state & TS_ISOPEN) == 0) { 93 tp->t_oproc = vcons_tty_start; 94 tp->t_param = vcons_tty_param; 95 tp->t_stop = nottystop; 96 tp->t_dev = dev; 97 98 tp->t_state |= TS_CARR_ON | TS_CONNECTED; 99 ttychars(tp); 100 tp->t_iflag = TTYDEF_IFLAG; 101 tp->t_oflag = TTYDEF_OFLAG; 102 tp->t_cflag = TTYDEF_CFLAG; 103 tp->t_lflag = TTYDEF_LFLAG; 104 tp->t_ispeed = TTYDEF_SPEED; 105 tp->t_ospeed = TTYDEF_SPEED; 106 ttsetwater(tp); 107 } 108 if (minor(dev) == 0) { 109 error = (*linesw[tp->t_line].l_open)(dev, tp); 110 ioctl(0, TIOCGWINSZ, &tp->t_winsize); 111 112 if (kqueue_console_info == NULL) 113 kqueue_console_info = kqueue_add(0, vcons_intr, tp); 114 } else { 115 /* dummy up other minors so the installer will run */ 116 error = 0; 117 } 118 lwkt_reltoken(&tty_token); 119 return(error); 120 } 121 122 static int 123 vcons_close(struct dev_close_args *ap) 124 { 125 cdev_t dev = ap->a_head.a_dev; 126 struct tty *tp; 127 128 lwkt_gettoken(&tty_token); 129 tp = dev->si_tty; 130 (*linesw[tp->t_line].l_close)(tp, ap->a_fflag); 131 ttyclose(tp); 132 lwkt_reltoken(&tty_token); 133 return(0); 134 } 135 136 static int 137 vcons_ioctl(struct dev_ioctl_args *ap) 138 { 139 cdev_t dev = ap->a_head.a_dev; 140 struct tty *tp; 141 int error; 142 143 lwkt_gettoken(&tty_token); 144 tp = dev->si_tty; 145 error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data, 146 ap->a_fflag, ap->a_cred); 147 if (error != ENOIOCTL) { 148 lwkt_reltoken(&tty_token); 149 return (error); 150 } 151 error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag); 152 if (error != ENOIOCTL) { 153 lwkt_reltoken(&tty_token); 154 return (error); 155 } 156 lwkt_reltoken(&tty_token); 157 return (ENOTTY); 158 } 159 160 static int 161 vcons_tty_param(struct tty *tp, struct termios *tio) 162 { 163 lwkt_gettoken(&tty_token); 164 tp->t_ispeed = tio->c_ispeed; 165 tp->t_ospeed = tio->c_ospeed; 166 tp->t_cflag = tio->c_cflag; 167 lwkt_reltoken(&tty_token); 168 return(0); 169 } 170 171 static void 172 vcons_tty_start(struct tty *tp) 173 { 174 int n; 175 char buf[64]; 176 177 lwkt_gettoken(&tty_token); 178 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 179 ttwwakeup(tp); 180 lwkt_reltoken(&tty_token); 181 return; 182 } 183 tp->t_state |= TS_BUSY; 184 while ((n = q_to_b(&tp->t_outq, buf, sizeof(buf))) > 0) { 185 /* 186 * Dummy up ttyv1, etc. 187 */ 188 if (minor(tp->t_dev) == 0) { 189 pwrite(1, buf, n, -1); 190 } 191 } 192 tp->t_state &= ~TS_BUSY; 193 lwkt_reltoken(&tty_token); 194 ttwwakeup(tp); 195 } 196 197 static 198 void 199 vcons_intr(void *tpx, struct intrframe *frame __unused) 200 { 201 struct tty *tp = tpx; 202 unsigned char buf[32]; 203 int i; 204 int n; 205 206 lwkt_gettoken(&tty_token); 207 /* 208 * If we aren't open we only have synchronous traffic via the 209 * debugger and do not need to poll. 210 */ 211 if ((tp->t_state & TS_ISOPEN) == 0) { 212 lwkt_reltoken(&tty_token); 213 return; 214 } 215 216 /* 217 * Only poll if we are open and haven't been stolen by the debugger. 218 */ 219 if (console_stolen_by_kernel == 0 && (tp->t_state & TS_ISOPEN)) { 220 do { 221 n = extpread(0, buf, sizeof(buf), O_FNONBLOCKING, -1LL); 222 for (i = 0; i < n; ++i) 223 (*linesw[tp->t_line].l_rint)(buf[i], tp); 224 } while (n > 0); 225 } 226 lwkt_reltoken(&tty_token); 227 } 228 229 /************************************************************************ 230 * KERNEL CONSOLE INTERFACE * 231 ************************************************************************ 232 * 233 * Kernel direct-call interface console driver 234 */ 235 static cn_probe_t vconsprobe; 236 static cn_init_t vconsinit; 237 static cn_init_fini_t vconsinit_fini; 238 static cn_term_t vconsterm; 239 static cn_getc_t vconsgetc; 240 static cn_checkc_t vconscheckc; 241 static cn_putc_t vconsputc; 242 243 CONS_DRIVER(vcons, vconsprobe, vconsinit, vconsinit_fini, vconsterm, vconsgetc, 244 vconscheckc, vconsputc, NULL, NULL); 245 246 static struct termios init_tio; 247 static struct consdev *vconsole; 248 249 static void 250 vconsprobe(struct consdev *cp) 251 { 252 cp->cn_pri = CN_NORMAL; 253 cp->cn_probegood = 1; 254 } 255 256 /* 257 * This is a little bulky handler to set proper terminal 258 * settings in the case of a signal which might lead to 259 * termination or suspension. 260 */ 261 static void 262 vconssignal(int sig) 263 { 264 struct termios curtio; 265 struct sigaction sa, osa; 266 sigset_t ss, oss; 267 268 tcgetattr(0, &curtio); 269 tcsetattr(0, TCSAFLUSH, &init_tio); 270 bzero(&sa, sizeof(sa)); 271 sigemptyset(&sa.sa_mask); 272 sa.sa_handler = SIG_DFL; 273 sigaction(sig, &sa, &osa); 274 sigemptyset(&ss); 275 sigaddset(&ss, sig); 276 sigprocmask(SIG_UNBLOCK, &ss, &oss); 277 raise(sig); /* now hand down the sig */ 278 sigprocmask(SIG_SETMASK, &oss, NULL); 279 sigaction(sig, &osa, NULL); 280 tcsetattr(0, TCSAFLUSH, &curtio); 281 } 282 283 static void 284 vconswinchsig(int __unused sig) 285 { 286 signalintr(3); 287 } 288 289 static void 290 vconswinch_intr(void *arg __unused, void *frame __unused) 291 { 292 struct winsize newsize; 293 294 if (vconsole != NULL && vconsole->cn_dev->si_tty != NULL) { 295 ioctl(0, TIOCGWINSZ, &newsize); 296 /* 297 * ttioctl(vconsole->cn_dev->si_tty, TIOCSWINSZ, &newsize, 0); 298 * I wished. Unfortunately this needs a curproc, so do it 299 * manually. 300 */ 301 if (bcmp(&newsize, &vconsole->cn_dev->si_tty->t_winsize, 302 sizeof(newsize)) != 0) { 303 vconsole->cn_dev->si_tty->t_winsize = newsize; 304 pgsignal(vconsole->cn_dev->si_tty->t_pgrp, SIGWINCH, 1); 305 } 306 } 307 } 308 309 static void 310 vconscleanup(void) 311 { 312 /* 313 * We might catch stray SIGIOs, so try hard. 314 */ 315 while (tcsetattr(0, TCSAFLUSH, &init_tio) != 0 && errno == EINTR) 316 /* NOTHING */; 317 } 318 319 static void 320 vconsinit(struct consdev *cp) 321 { 322 struct sigaction sa; 323 324 vconsole = cp; 325 326 tcgetattr(0, &init_tio); 327 bzero(&sa, sizeof(sa)); 328 sigemptyset(&sa.sa_mask); 329 sa.sa_handler = vconssignal; 330 sigaction(SIGTSTP, &sa, NULL); 331 sigaction(SIGINT, &sa, NULL); 332 sigaction(SIGTERM, &sa, NULL); 333 atexit(vconscleanup); 334 vcons_set_mode(0); 335 } 336 337 static void 338 vconsinit_fini(struct consdev *cp) 339 { 340 struct sigaction sa; 341 cdev_t dev; 342 int i; 343 344 /* 345 * We have to do this here rather then in early boot to be able 346 * to use the interrupt subsystem. 347 */ 348 register_int_virtual(3, vconswinch_intr, NULL, "swinch", NULL, 349 INTR_MPSAFE); 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