1 /* $NetBSD: cons.c,v 1.95 2023/09/02 17:44:59 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1990, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: Utah $Hdr: cons.c 1.7 92/01/21$ 37 * 38 * @(#)cons.c 8.2 (Berkeley) 1/12/94 39 */ 40 41 #include <sys/cdefs.h> 42 __KERNEL_RCSID(0, "$NetBSD: cons.c,v 1.95 2023/09/02 17:44:59 riastradh Exp $"); 43 44 #include <sys/param.h> 45 46 #include <sys/atomic.h> 47 #include <sys/buf.h> 48 #include <sys/conf.h> 49 #include <sys/file.h> 50 #include <sys/heartbeat.h> 51 #include <sys/ioctl.h> 52 #include <sys/kauth.h> 53 #include <sys/module.h> 54 #include <sys/mutex.h> 55 #include <sys/poll.h> 56 #include <sys/proc.h> 57 #include <sys/pserialize.h> 58 #include <sys/systm.h> 59 #include <sys/tty.h> 60 #include <sys/vnode.h> 61 62 #include <dev/cons.h> 63 64 #include "nullcons.h" 65 66 dev_type_open(cnopen); 67 dev_type_close(cnclose); 68 dev_type_read(cnread); 69 dev_type_write(cnwrite); 70 dev_type_ioctl(cnioctl); 71 dev_type_poll(cnpoll); 72 dev_type_kqfilter(cnkqfilter); 73 74 static bool cn_redirect(dev_t *, int, int *, struct tty **); 75 static void cn_release(struct tty *); 76 77 const struct cdevsw cons_cdevsw = { 78 .d_open = cnopen, 79 .d_close = cnclose, 80 .d_read = cnread, 81 .d_write = cnwrite, 82 .d_ioctl = cnioctl, 83 .d_stop = nostop, 84 .d_tty = notty, 85 .d_poll = cnpoll, 86 .d_mmap = nommap, 87 .d_kqfilter = cnkqfilter, 88 .d_discard = nodiscard, 89 .d_flag = D_TTY|D_MPSAFE, 90 }; 91 92 static struct kmutex cn_lock; 93 94 struct tty *volatile constty; /* virtual console output device */ 95 struct consdev *cn_tab; /* physical console device info */ 96 struct vnode *cn_devvp[2]; /* vnode for underlying device. */ 97 98 void 99 cn_set_tab(struct consdev *tab) 100 { 101 102 /* 103 * This is a point that we should have KASSERT(cold) or add 104 * synchronization in case this can happen after cold boot. 105 * However, cn_tab initialization is so critical to any 106 * diagnostics or debugging that we need to tread carefully 107 * about introducing new ways to crash. So let's put the 108 * assertion in only after we've audited most or all of the 109 * cn_tab updates. 110 */ 111 cn_tab = tab; 112 } 113 114 int 115 cnopen(dev_t dev, int flag, int mode, struct lwp *l) 116 { 117 dev_t cndev; 118 int unit, error; 119 120 unit = minor(dev); 121 if (unit > 1) 122 return ENODEV; 123 124 mutex_enter(&cn_lock); 125 126 if (cn_tab == NULL) { 127 error = 0; 128 goto out; 129 } 130 131 /* 132 * always open the 'real' console device, so we don't get nailed 133 * later. This follows normal device semantics; they always get 134 * open() calls. 135 */ 136 cndev = cn_tab->cn_dev; 137 #if NNULLCONS > 0 138 if (cndev == NODEV) { 139 nullconsattach(0); 140 } 141 #else /* NNULLCONS > 0 */ 142 if (cndev == NODEV) { 143 /* 144 * This is most likely an error in the console attach 145 * code. Panicking looks better than jumping into nowhere 146 * through cdevsw below.... 147 */ 148 panic("cnopen: no console device"); 149 } 150 #endif /* NNULLCONS > 0 */ 151 if (dev == cndev) { 152 /* 153 * This causes cnopen() to be called recursively, which 154 * is generally a bad thing. It is often caused when 155 * dev == 0 and cn_dev has not been set, but was probably 156 * initialised to 0. 157 */ 158 panic("cnopen: cn_tab->cn_dev == dev"); 159 } 160 if (cn_devvp[unit] != NULLVP) { 161 error = 0; 162 goto out; 163 } 164 if ((error = cdevvp(cndev, &cn_devvp[unit])) != 0) { 165 printf("cnopen: unable to get vnode reference\n"); 166 goto out; 167 } 168 vn_lock(cn_devvp[unit], LK_EXCLUSIVE | LK_RETRY); 169 error = VOP_OPEN(cn_devvp[unit], flag, kauth_cred_get()); 170 VOP_UNLOCK(cn_devvp[unit]); 171 172 out: mutex_exit(&cn_lock); 173 return error; 174 } 175 176 int 177 cnclose(dev_t dev, int flag, int mode, struct lwp *l) 178 { 179 struct vnode *vp; 180 int unit, error; 181 182 unit = minor(dev); 183 if (unit > 1) 184 return ENODEV; 185 186 mutex_enter(&cn_lock); 187 188 if (cn_tab == NULL) { 189 error = 0; 190 goto out; 191 } 192 193 vp = cn_devvp[unit]; 194 cn_devvp[unit] = NULL; 195 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 196 error = VOP_CLOSE(vp, flag, kauth_cred_get()); 197 VOP_UNLOCK(vp); 198 vrele(vp); 199 200 out: mutex_exit(&cn_lock); 201 return error; 202 } 203 204 int 205 cnread(dev_t dev, struct uio *uio, int flag) 206 { 207 struct tty *ctp = NULL; 208 int error; 209 210 /* 211 * If we would redirect input, punt. This will keep strange 212 * things from happening to people who are using the real 213 * console. Nothing should be using /dev/console for 214 * input (except a shell in single-user mode, but then, 215 * one wouldn't TIOCCONS then). 216 */ 217 if (!cn_redirect(&dev, 1, &error, &ctp)) 218 return error; 219 error = cdev_read(dev, uio, flag); 220 cn_release(ctp); 221 return error; 222 } 223 224 int 225 cnwrite(dev_t dev, struct uio *uio, int flag) 226 { 227 struct tty *ctp = NULL; 228 int error; 229 230 /* Redirect output, if that's appropriate. */ 231 if (!cn_redirect(&dev, 0, &error, &ctp)) 232 return error; 233 error = cdev_write(dev, uio, flag); 234 cn_release(ctp); 235 return error; 236 } 237 238 int 239 cnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 240 { 241 struct tty *ctp = NULL; 242 int error; 243 244 error = 0; 245 246 /* 247 * Superuser can always use this to wrest control of console 248 * output from the "virtual" console. 249 */ 250 if (cmd == TIOCCONS) { 251 struct tty *tp; 252 253 mutex_enter(&constty_lock); 254 tp = atomic_load_relaxed(&constty); 255 if (tp == NULL) { 256 mutex_exit(&constty_lock); 257 goto passthrough; /* XXX ??? */ 258 } 259 error = kauth_authorize_device_tty(l->l_cred, 260 KAUTH_DEVICE_TTY_VIRTUAL, tp); 261 if (!error) 262 atomic_store_relaxed(&constty, NULL); 263 mutex_exit(&constty_lock); 264 return error; 265 } 266 passthrough: 267 /* 268 * Redirect the ioctl, if that's appropriate. 269 * Note that strange things can happen, if a program does 270 * ioctls on /dev/console, then the console is redirected 271 * out from under it. 272 */ 273 if (!cn_redirect(&dev, 0, &error, &ctp)) 274 return error; 275 error = cdev_ioctl(dev, cmd, data, flag, l); 276 cn_release(ctp); 277 return error; 278 } 279 280 /*ARGSUSED*/ 281 int 282 cnpoll(dev_t dev, int events, struct lwp *l) 283 { 284 struct tty *ctp = NULL; 285 int error; 286 287 /* 288 * Redirect the poll, if that's appropriate. 289 * I don't want to think of the possible side effects 290 * of console redirection here. 291 */ 292 if (!cn_redirect(&dev, 0, &error, &ctp)) 293 return POLLHUP; 294 error = cdev_poll(dev, events, l); 295 cn_release(ctp); 296 return error; 297 } 298 299 /*ARGSUSED*/ 300 int 301 cnkqfilter(dev_t dev, struct knote *kn) 302 { 303 struct tty *ctp = NULL; 304 int error; 305 306 /* 307 * Redirect the kqfilter, if that's appropriate. 308 * I don't want to think of the possible side effects 309 * of console redirection here. 310 */ 311 if (!cn_redirect(&dev, 0, &error, &ctp)) 312 return error; 313 error = cdev_kqfilter(dev, kn); 314 cn_release(ctp); 315 return error; 316 } 317 318 int 319 cngetc(void) 320 { 321 if (cn_tab == NULL) 322 return (0); 323 int s = splhigh(); 324 for (;;) { 325 const int rv = (*cn_tab->cn_getc)(cn_tab->cn_dev); 326 if (rv >= 0) { 327 splx(s); 328 return rv; 329 } 330 docritpollhooks(); 331 } 332 } 333 334 int 335 cngetsn(char *cp, int size) 336 { 337 char *lp; 338 int c, len; 339 340 cnpollc(1); 341 342 lp = cp; 343 len = 0; 344 for (;;) { 345 c = cngetc(); 346 switch (c) { 347 case '\n': 348 case '\r': 349 printf("\n"); 350 *lp++ = '\0'; 351 cnpollc(0); 352 return (len); 353 case '\b': 354 case '\177': 355 case '#': 356 if (len) { 357 --len; 358 --lp; 359 printf("\b \b"); 360 } 361 continue; 362 case '@': 363 case 'u'&037: /* CTRL-u */ 364 len = 0; 365 lp = cp; 366 printf("\n"); 367 continue; 368 default: 369 if (len + 1 >= size || c < ' ') { 370 printf("\007"); 371 continue; 372 } 373 printf("%c", c); 374 ++len; 375 *lp++ = c; 376 } 377 } 378 } 379 380 void 381 cnputc(int c) 382 { 383 384 if (cn_tab == NULL) 385 return; 386 387 /* 388 * XXX 389 * for some reason this causes ARCS firmware to output an endless stream of 390 * whitespaces with n32 kernels, so use the pre-1.74 code for now until I can 391 * figure out why this happens 392 */ 393 #ifndef sgimips 394 if (c) { 395 if (c == '\n') { 396 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); 397 docritpollhooks(); 398 } 399 (*cn_tab->cn_putc)(cn_tab->cn_dev, c); 400 } 401 #else 402 if (c) { 403 (*cn_tab->cn_putc)(cn_tab->cn_dev, c); 404 if (c == '\n') { 405 docritpollhooks(); 406 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); 407 } 408 } 409 #endif 410 } 411 412 void 413 cnpollc(int on) 414 { 415 static int refcount = 0; 416 417 if (cn_tab == NULL) 418 return; 419 if (!on) 420 --refcount; 421 if (refcount == 0) { 422 if (on) { 423 /* 424 * Bind to the current CPU by disabling 425 * preemption (more convenient than finding a 426 * place to store a stack to unwind for 427 * curlwp_bind/bindx, and preemption wouldn't 428 * happen anyway while spinning at high IPL in 429 * cngetc) so that curcpu() is stable so that 430 * we can suspend heartbeat checks for it. 431 */ 432 kpreempt_disable(); 433 heartbeat_suspend(); 434 } 435 (*cn_tab->cn_pollc)(cn_tab->cn_dev, on); 436 if (!on) { 437 heartbeat_resume(); 438 kpreempt_enable(); 439 } 440 } 441 if (on) 442 ++refcount; 443 } 444 445 void 446 nullcnpollc(dev_t dev, int on) 447 { 448 449 } 450 451 void 452 cnbell(u_int pitch, u_int period, u_int volume) 453 { 454 455 if (cn_tab == NULL || cn_tab->cn_bell == NULL) 456 return; 457 (*cn_tab->cn_bell)(cn_tab->cn_dev, pitch, period, volume); 458 } 459 460 void 461 cnflush(void) 462 { 463 if (cn_tab == NULL || cn_tab->cn_flush == NULL) 464 return; 465 (*cn_tab->cn_flush)(cn_tab->cn_dev); 466 } 467 468 void 469 cnhalt(void) 470 { 471 if (cn_tab == NULL || cn_tab->cn_halt == NULL) 472 return; 473 (*cn_tab->cn_halt)(cn_tab->cn_dev); 474 } 475 476 /* 477 * Redirect output, if that's appropriate. If there's no real console, 478 * return ENXIO. 479 */ 480 static bool 481 cn_redirect(dev_t *devp, int is_read, int *error, struct tty **ctpp) 482 { 483 dev_t dev = *devp; 484 struct tty *ctp; 485 int s; 486 bool ok = false; 487 488 *error = ENXIO; 489 *ctpp = NULL; 490 s = pserialize_read_enter(); 491 if ((ctp = atomic_load_consume(&constty)) != NULL && minor(dev) == 0 && 492 (cn_tab == NULL || (cn_tab->cn_pri != CN_REMOTE))) { 493 if (is_read) { 494 *error = 0; 495 goto out; 496 } 497 tty_acquire(ctp); 498 *ctpp = ctp; 499 dev = ctp->t_dev; 500 } else if (cn_tab == NULL) 501 goto out; 502 else 503 dev = cn_tab->cn_dev; 504 ok = true; 505 *devp = dev; 506 out: pserialize_read_exit(s); 507 return ok; 508 } 509 510 static void 511 cn_release(struct tty *ctp) 512 { 513 514 if (ctp == NULL) 515 return; 516 tty_release(ctp); 517 } 518 519 MODULE(MODULE_CLASS_DRIVER, cons, NULL); 520 521 static int 522 cons_modcmd(modcmd_t cmd, void *arg) 523 { 524 525 switch (cmd) { 526 case MODULE_CMD_INIT: 527 mutex_init(&cn_lock, MUTEX_DEFAULT, IPL_NONE); 528 return 0; 529 case MODULE_CMD_FINI: 530 mutex_destroy(&cn_lock); 531 return 0; 532 default: 533 return ENOTTY; 534 } 535 } 536