1 /* $NetBSD: cons.c,v 1.92 2022/10/25 23:21:33 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.92 2022/10/25 23:21:33 riastradh Exp $"); 43 44 #include <sys/param.h> 45 #include <sys/proc.h> 46 #include <sys/systm.h> 47 #include <sys/buf.h> 48 #include <sys/ioctl.h> 49 #include <sys/poll.h> 50 #include <sys/tty.h> 51 #include <sys/file.h> 52 #include <sys/conf.h> 53 #include <sys/vnode.h> 54 #include <sys/kauth.h> 55 #include <sys/mutex.h> 56 #include <sys/module.h> 57 #include <sys/atomic.h> 58 #include <sys/pserialize.h> 59 60 #include <dev/cons.h> 61 62 #include "nullcons.h" 63 64 dev_type_open(cnopen); 65 dev_type_close(cnclose); 66 dev_type_read(cnread); 67 dev_type_write(cnwrite); 68 dev_type_ioctl(cnioctl); 69 dev_type_poll(cnpoll); 70 dev_type_kqfilter(cnkqfilter); 71 72 static bool cn_redirect(dev_t *, int, int *, struct tty **); 73 static void cn_release(struct tty *); 74 75 const struct cdevsw cons_cdevsw = { 76 .d_open = cnopen, 77 .d_close = cnclose, 78 .d_read = cnread, 79 .d_write = cnwrite, 80 .d_ioctl = cnioctl, 81 .d_stop = nostop, 82 .d_tty = notty, 83 .d_poll = cnpoll, 84 .d_mmap = nommap, 85 .d_kqfilter = cnkqfilter, 86 .d_discard = nodiscard, 87 .d_flag = D_TTY|D_MPSAFE, 88 }; 89 90 static struct kmutex cn_lock; 91 92 struct tty *volatile constty; /* virtual console output device */ 93 struct consdev *cn_tab; /* physical console device info */ 94 struct vnode *cn_devvp[2]; /* vnode for underlying device. */ 95 96 void 97 cn_set_tab(struct consdev *tab) 98 { 99 100 /* 101 * This is a point that we should have KASSERT(cold) or add 102 * synchronization in case this can happen after cold boot. 103 * However, cn_tab initialization is so critical to any 104 * diagnostics or debugging that we need to tread carefully 105 * about introducing new ways to crash. So let's put the 106 * assertion in only after we've audited most or all of the 107 * cn_tab updates. 108 */ 109 cn_tab = tab; 110 } 111 112 int 113 cnopen(dev_t dev, int flag, int mode, struct lwp *l) 114 { 115 dev_t cndev; 116 int unit, error; 117 118 unit = minor(dev); 119 if (unit > 1) 120 return ENODEV; 121 122 mutex_enter(&cn_lock); 123 124 if (cn_tab == NULL) { 125 error = 0; 126 goto out; 127 } 128 129 /* 130 * always open the 'real' console device, so we don't get nailed 131 * later. This follows normal device semantics; they always get 132 * open() calls. 133 */ 134 cndev = cn_tab->cn_dev; 135 #if NNULLCONS > 0 136 if (cndev == NODEV) { 137 nullconsattach(0); 138 } 139 #else /* NNULLCONS > 0 */ 140 if (cndev == NODEV) { 141 /* 142 * This is most likely an error in the console attach 143 * code. Panicking looks better than jumping into nowhere 144 * through cdevsw below.... 145 */ 146 panic("cnopen: no console device"); 147 } 148 #endif /* NNULLCONS > 0 */ 149 if (dev == cndev) { 150 /* 151 * This causes cnopen() to be called recursively, which 152 * is generally a bad thing. It is often caused when 153 * dev == 0 and cn_dev has not been set, but was probably 154 * initialised to 0. 155 */ 156 panic("cnopen: cn_tab->cn_dev == dev"); 157 } 158 if (cn_devvp[unit] != NULLVP) { 159 error = 0; 160 goto out; 161 } 162 if ((error = cdevvp(cndev, &cn_devvp[unit])) != 0) { 163 printf("cnopen: unable to get vnode reference\n"); 164 goto out; 165 } 166 vn_lock(cn_devvp[unit], LK_EXCLUSIVE | LK_RETRY); 167 error = VOP_OPEN(cn_devvp[unit], flag, kauth_cred_get()); 168 VOP_UNLOCK(cn_devvp[unit]); 169 170 out: mutex_exit(&cn_lock); 171 return error; 172 } 173 174 int 175 cnclose(dev_t dev, int flag, int mode, struct lwp *l) 176 { 177 struct vnode *vp; 178 int unit, error; 179 180 unit = minor(dev); 181 if (unit > 1) 182 return ENODEV; 183 184 mutex_enter(&cn_lock); 185 186 if (cn_tab == NULL) { 187 error = 0; 188 goto out; 189 } 190 191 vp = cn_devvp[unit]; 192 cn_devvp[unit] = NULL; 193 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 194 error = VOP_CLOSE(vp, flag, kauth_cred_get()); 195 VOP_UNLOCK(vp); 196 vrele(vp); 197 198 out: mutex_exit(&cn_lock); 199 return error; 200 } 201 202 int 203 cnread(dev_t dev, struct uio *uio, int flag) 204 { 205 struct tty *ctp = NULL; 206 int error; 207 208 /* 209 * If we would redirect input, punt. This will keep strange 210 * things from happening to people who are using the real 211 * console. Nothing should be using /dev/console for 212 * input (except a shell in single-user mode, but then, 213 * one wouldn't TIOCCONS then). 214 */ 215 if (!cn_redirect(&dev, 1, &error, &ctp)) 216 return error; 217 error = cdev_read(dev, uio, flag); 218 cn_release(ctp); 219 return error; 220 } 221 222 int 223 cnwrite(dev_t dev, struct uio *uio, int flag) 224 { 225 struct tty *ctp = NULL; 226 int error; 227 228 /* Redirect output, if that's appropriate. */ 229 if (!cn_redirect(&dev, 0, &error, &ctp)) 230 return error; 231 error = cdev_write(dev, uio, flag); 232 cn_release(ctp); 233 return error; 234 } 235 236 int 237 cnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 238 { 239 struct tty *ctp = NULL; 240 int error; 241 242 error = 0; 243 244 /* 245 * Superuser can always use this to wrest control of console 246 * output from the "virtual" console. 247 */ 248 if (cmd == TIOCCONS) { 249 struct tty *tp; 250 251 mutex_enter(&constty_lock); 252 tp = atomic_load_relaxed(&constty); 253 if (tp == NULL) { 254 mutex_exit(&constty_lock); 255 goto passthrough; /* XXX ??? */ 256 } 257 error = kauth_authorize_device_tty(l->l_cred, 258 KAUTH_DEVICE_TTY_VIRTUAL, tp); 259 if (!error) 260 atomic_store_relaxed(&constty, NULL); 261 mutex_exit(&constty_lock); 262 return error; 263 } 264 passthrough: 265 /* 266 * Redirect the ioctl, if that's appropriate. 267 * Note that strange things can happen, if a program does 268 * ioctls on /dev/console, then the console is redirected 269 * out from under it. 270 */ 271 if (!cn_redirect(&dev, 0, &error, &ctp)) 272 return error; 273 error = cdev_ioctl(dev, cmd, data, flag, l); 274 cn_release(ctp); 275 return error; 276 } 277 278 /*ARGSUSED*/ 279 int 280 cnpoll(dev_t dev, int events, struct lwp *l) 281 { 282 struct tty *ctp = NULL; 283 int error; 284 285 /* 286 * Redirect the poll, if that's appropriate. 287 * I don't want to think of the possible side effects 288 * of console redirection here. 289 */ 290 if (!cn_redirect(&dev, 0, &error, &ctp)) 291 return POLLHUP; 292 error = cdev_poll(dev, events, l); 293 cn_release(ctp); 294 return error; 295 } 296 297 /*ARGSUSED*/ 298 int 299 cnkqfilter(dev_t dev, struct knote *kn) 300 { 301 struct tty *ctp = NULL; 302 int error; 303 304 /* 305 * Redirect the kqfilter, if that's appropriate. 306 * I don't want to think of the possible side effects 307 * of console redirection here. 308 */ 309 if (!cn_redirect(&dev, 0, &error, &ctp)) 310 return error; 311 error = cdev_kqfilter(dev, kn); 312 cn_release(ctp); 313 return error; 314 } 315 316 int 317 cngetc(void) 318 { 319 if (cn_tab == NULL) 320 return (0); 321 int s = splhigh(); 322 for (;;) { 323 const int rv = (*cn_tab->cn_getc)(cn_tab->cn_dev); 324 if (rv >= 0) { 325 splx(s); 326 return rv; 327 } 328 docritpollhooks(); 329 } 330 } 331 332 int 333 cngetsn(char *cp, int size) 334 { 335 char *lp; 336 int c, len; 337 338 cnpollc(1); 339 340 lp = cp; 341 len = 0; 342 for (;;) { 343 c = cngetc(); 344 switch (c) { 345 case '\n': 346 case '\r': 347 printf("\n"); 348 *lp++ = '\0'; 349 cnpollc(0); 350 return (len); 351 case '\b': 352 case '\177': 353 case '#': 354 if (len) { 355 --len; 356 --lp; 357 printf("\b \b"); 358 } 359 continue; 360 case '@': 361 case 'u'&037: /* CTRL-u */ 362 len = 0; 363 lp = cp; 364 printf("\n"); 365 continue; 366 default: 367 if (len + 1 >= size || c < ' ') { 368 printf("\007"); 369 continue; 370 } 371 printf("%c", c); 372 ++len; 373 *lp++ = c; 374 } 375 } 376 } 377 378 void 379 cnputc(int c) 380 { 381 382 if (cn_tab == NULL) 383 return; 384 385 /* 386 * XXX 387 * for some reason this causes ARCS firmware to output an endless stream of 388 * whitespaces with n32 kernels, so use the pre-1.74 code for now until I can 389 * figure out why this happens 390 */ 391 #ifndef sgimips 392 if (c) { 393 if (c == '\n') { 394 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); 395 docritpollhooks(); 396 } 397 (*cn_tab->cn_putc)(cn_tab->cn_dev, c); 398 } 399 #else 400 if (c) { 401 (*cn_tab->cn_putc)(cn_tab->cn_dev, c); 402 if (c == '\n') { 403 docritpollhooks(); 404 (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); 405 } 406 } 407 #endif 408 } 409 410 void 411 cnpollc(int on) 412 { 413 static int refcount = 0; 414 415 if (cn_tab == NULL) 416 return; 417 if (!on) 418 --refcount; 419 if (refcount == 0) 420 (*cn_tab->cn_pollc)(cn_tab->cn_dev, on); 421 if (on) 422 ++refcount; 423 } 424 425 void 426 nullcnpollc(dev_t dev, int on) 427 { 428 429 } 430 431 void 432 cnbell(u_int pitch, u_int period, u_int volume) 433 { 434 435 if (cn_tab == NULL || cn_tab->cn_bell == NULL) 436 return; 437 (*cn_tab->cn_bell)(cn_tab->cn_dev, pitch, period, volume); 438 } 439 440 void 441 cnflush(void) 442 { 443 if (cn_tab == NULL || cn_tab->cn_flush == NULL) 444 return; 445 (*cn_tab->cn_flush)(cn_tab->cn_dev); 446 } 447 448 void 449 cnhalt(void) 450 { 451 if (cn_tab == NULL || cn_tab->cn_halt == NULL) 452 return; 453 (*cn_tab->cn_halt)(cn_tab->cn_dev); 454 } 455 456 /* 457 * Redirect output, if that's appropriate. If there's no real console, 458 * return ENXIO. 459 */ 460 static bool 461 cn_redirect(dev_t *devp, int is_read, int *error, struct tty **ctpp) 462 { 463 dev_t dev = *devp; 464 struct tty *ctp; 465 int s; 466 bool ok = false; 467 468 *error = ENXIO; 469 *ctpp = NULL; 470 s = pserialize_read_enter(); 471 if ((ctp = atomic_load_consume(&constty)) != NULL && minor(dev) == 0 && 472 (cn_tab == NULL || (cn_tab->cn_pri != CN_REMOTE))) { 473 if (is_read) { 474 *error = 0; 475 goto out; 476 } 477 tty_acquire(ctp); 478 *ctpp = ctp; 479 dev = ctp->t_dev; 480 } else if (cn_tab == NULL) 481 goto out; 482 else 483 dev = cn_tab->cn_dev; 484 ok = true; 485 *devp = dev; 486 out: pserialize_read_exit(s); 487 return ok; 488 } 489 490 static void 491 cn_release(struct tty *ctp) 492 { 493 494 if (ctp == NULL) 495 return; 496 tty_release(ctp); 497 } 498 499 MODULE(MODULE_CLASS_DRIVER, cons, NULL); 500 501 static int 502 cons_modcmd(modcmd_t cmd, void *arg) 503 { 504 505 switch (cmd) { 506 case MODULE_CMD_INIT: 507 mutex_init(&cn_lock, MUTEX_DEFAULT, IPL_NONE); 508 return 0; 509 case MODULE_CMD_FINI: 510 mutex_destroy(&cn_lock); 511 return 0; 512 default: 513 return ENOTTY; 514 } 515 } 516