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