xref: /freebsd/sys/kern/kern_cons.c (revision 9268022b74279434ed6300244e3f977e56a8ceb5)
1 /*-
2  * Copyright (c) 1988 University of Utah.
3  * Copyright (c) 1991 The Regents of the University of California.
4  * Copyright (c) 1999 Michael Smith
5  * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
6  *
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * the Systems Programming Group of the University of Utah Computer
11  * Science Department.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
38  */
39 
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42 
43 #include "opt_ddb.h"
44 #include "opt_syscons.h"
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/lock.h>
49 #include <sys/mutex.h>
50 #include <sys/conf.h>
51 #include <sys/cons.h>
52 #include <sys/fcntl.h>
53 #include <sys/kdb.h>
54 #include <sys/kernel.h>
55 #include <sys/malloc.h>
56 #include <sys/msgbuf.h>
57 #include <sys/namei.h>
58 #include <sys/priv.h>
59 #include <sys/proc.h>
60 #include <sys/queue.h>
61 #include <sys/reboot.h>
62 #include <sys/sysctl.h>
63 #include <sys/sbuf.h>
64 #include <sys/tty.h>
65 #include <sys/uio.h>
66 #include <sys/vnode.h>
67 
68 #include <ddb/ddb.h>
69 
70 #include <machine/cpu.h>
71 #include <machine/clock.h>
72 
73 static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
74 
75 struct cn_device {
76 	STAILQ_ENTRY(cn_device) cnd_next;
77 	struct		consdev *cnd_cn;
78 };
79 
80 #define CNDEVPATHMAX	32
81 #define CNDEVTAB_SIZE	4
82 static struct cn_device cn_devtab[CNDEVTAB_SIZE];
83 static STAILQ_HEAD(, cn_device) cn_devlist =
84     STAILQ_HEAD_INITIALIZER(cn_devlist);
85 
86 int	cons_avail_mask = 0;	/* Bit mask. Each registered low level console
87 				 * which is currently unavailable for inpit
88 				 * (i.e., if it is in graphics mode) will have
89 				 * this bit cleared.
90 				 */
91 static int cn_mute;
92 static char *consbuf;			/* buffer used by `consmsgbuf' */
93 static struct callout conscallout;	/* callout for outputting to constty */
94 struct msgbuf consmsgbuf;		/* message buffer for console tty */
95 static u_char console_pausing;		/* pause after each line during probe */
96 static char *console_pausestr=
97 "<pause; press any key to proceed to next line or '.' to end pause mode>";
98 struct tty *constty;			/* pointer to console "window" tty */
99 static struct mtx cnputs_mtx;		/* Mutex for cnputs(). */
100 static int use_cnputs_mtx = 0;		/* != 0 if cnputs_mtx locking reqd. */
101 
102 static void constty_timeout(void *arg);
103 
104 static struct consdev cons_consdev;
105 DATA_SET(cons_set, cons_consdev);
106 SET_DECLARE(cons_set, struct consdev);
107 
108 void
109 cninit(void)
110 {
111 	struct consdev *best_cn, *cn, **list;
112 
113 	/*
114 	 * Check if we should mute the console (for security reasons perhaps)
115 	 * It can be changes dynamically using sysctl kern.consmute
116 	 * once we are up and going.
117 	 *
118 	 */
119         cn_mute = ((boothowto & (RB_MUTE
120 			|RB_SINGLE
121 			|RB_VERBOSE
122 			|RB_ASKNAME)) == RB_MUTE);
123 
124 	/*
125 	 * Find the first console with the highest priority.
126 	 */
127 	best_cn = NULL;
128 	SET_FOREACH(list, cons_set) {
129 		cn = *list;
130 		cnremove(cn);
131 		/* Skip cons_consdev. */
132 		if (cn->cn_ops == NULL)
133 			continue;
134 		cn->cn_ops->cn_probe(cn);
135 		if (cn->cn_pri == CN_DEAD)
136 			continue;
137 		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
138 			best_cn = cn;
139 		if (boothowto & RB_MULTIPLE) {
140 			/*
141 			 * Initialize console, and attach to it.
142 			 */
143 			cn->cn_ops->cn_init(cn);
144 			cnadd(cn);
145 		}
146 	}
147 	if (best_cn == NULL)
148 		return;
149 	if ((boothowto & RB_MULTIPLE) == 0) {
150 		best_cn->cn_ops->cn_init(best_cn);
151 		cnadd(best_cn);
152 	}
153 	if (boothowto & RB_PAUSE)
154 		console_pausing = 1;
155 	/*
156 	 * Make the best console the preferred console.
157 	 */
158 	cnselect(best_cn);
159 }
160 
161 void
162 cninit_finish()
163 {
164 	console_pausing = 0;
165 }
166 
167 /* add a new physical console to back the virtual console */
168 int
169 cnadd(struct consdev *cn)
170 {
171 	struct cn_device *cnd;
172 	int i;
173 
174 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
175 		if (cnd->cnd_cn == cn)
176 			return (0);
177 	for (i = 0; i < CNDEVTAB_SIZE; i++) {
178 		cnd = &cn_devtab[i];
179 		if (cnd->cnd_cn == NULL)
180 			break;
181 	}
182 	if (cnd->cnd_cn != NULL)
183 		return (ENOMEM);
184 	cnd->cnd_cn = cn;
185 	if (cn->cn_name[0] == '\0') {
186 		/* XXX: it is unclear if/where this print might output */
187 		printf("WARNING: console at %p has no name\n", cn);
188 	}
189 	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
190 	if (STAILQ_FIRST(&cn_devlist) == cnd)
191 		ttyconsdev_select(cnd->cnd_cn->cn_name);
192 
193 	/* Add device to the active mask. */
194 	cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
195 
196 	return (0);
197 }
198 
199 void
200 cnremove(struct consdev *cn)
201 {
202 	struct cn_device *cnd;
203 	int i;
204 
205 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
206 		if (cnd->cnd_cn != cn)
207 			continue;
208 		if (STAILQ_FIRST(&cn_devlist) == cnd)
209 			ttyconsdev_select(NULL);
210 		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
211 		cnd->cnd_cn = NULL;
212 
213 		/* Remove this device from available mask. */
214 		for (i = 0; i < CNDEVTAB_SIZE; i++)
215 			if (cnd == &cn_devtab[i]) {
216 				cons_avail_mask &= ~(1 << i);
217 				break;
218 			}
219 #if 0
220 		/*
221 		 * XXX
222 		 * syscons gets really confused if console resources are
223 		 * freed after the system has initialized.
224 		 */
225 		if (cn->cn_term != NULL)
226 			cn->cn_ops->cn_term(cn);
227 #endif
228 		return;
229 	}
230 }
231 
232 void
233 cnselect(struct consdev *cn)
234 {
235 	struct cn_device *cnd;
236 
237 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
238 		if (cnd->cnd_cn != cn)
239 			continue;
240 		if (cnd == STAILQ_FIRST(&cn_devlist))
241 			return;
242 		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
243 		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
244 		ttyconsdev_select(cnd->cnd_cn->cn_name);
245 		return;
246 	}
247 }
248 
249 void
250 cnavailable(struct consdev *cn, int available)
251 {
252 	int i;
253 
254 	for (i = 0; i < CNDEVTAB_SIZE; i++) {
255 		if (cn_devtab[i].cnd_cn == cn)
256 			break;
257 	}
258 	if (available) {
259 		if (i < CNDEVTAB_SIZE)
260 			cons_avail_mask |= (1 << i);
261 		cn->cn_flags &= ~CN_FLAG_NOAVAIL;
262 	} else {
263 		if (i < CNDEVTAB_SIZE)
264 			cons_avail_mask &= ~(1 << i);
265 		cn->cn_flags |= CN_FLAG_NOAVAIL;
266 	}
267 }
268 
269 int
270 cnunavailable(void)
271 {
272 
273 	return (cons_avail_mask == 0);
274 }
275 
276 /*
277  * sysctl_kern_console() provides output parseable in conscontrol(1).
278  */
279 static int
280 sysctl_kern_console(SYSCTL_HANDLER_ARGS)
281 {
282 	struct cn_device *cnd;
283 	struct consdev *cp, **list;
284 	char *p;
285 	int delete, error;
286 	struct sbuf *sb;
287 
288 	sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND);
289 	if (sb == NULL)
290 		return (ENOMEM);
291 	sbuf_clear(sb);
292 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
293 		sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name);
294 	sbuf_printf(sb, "/");
295 	SET_FOREACH(list, cons_set) {
296 		cp = *list;
297 		if (cp->cn_name[0] != '\0')
298 			sbuf_printf(sb, "%s,", cp->cn_name);
299 	}
300 	sbuf_finish(sb);
301 	error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
302 	if (error == 0 && req->newptr != NULL) {
303 		p = sbuf_data(sb);
304 		error = ENXIO;
305 		delete = 0;
306 		if (*p == '-') {
307 			delete = 1;
308 			p++;
309 		}
310 		SET_FOREACH(list, cons_set) {
311 			cp = *list;
312 			if (strcmp(p, cp->cn_name) != 0)
313 				continue;
314 			if (delete) {
315 				cnremove(cp);
316 				error = 0;
317 			} else {
318 				error = cnadd(cp);
319 				if (error == 0)
320 					cnselect(cp);
321 			}
322 			break;
323 		}
324 	}
325 	sbuf_delete(sb);
326 	return (error);
327 }
328 
329 SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
330 	0, 0, sysctl_kern_console, "A", "Console device control");
331 
332 /*
333  * User has changed the state of the console muting.
334  * This may require us to open or close the device in question.
335  */
336 static int
337 sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
338 {
339 	int error;
340 
341 	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
342 	if (error != 0 || req->newptr == NULL)
343 		return (error);
344 	return (error);
345 }
346 
347 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
348 	0, sizeof(cn_mute), sysctl_kern_consmute, "I",
349 	"State of the console muting");
350 
351 void
352 cngrab()
353 {
354 	struct cn_device *cnd;
355 	struct consdev *cn;
356 
357 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
358 		cn = cnd->cnd_cn;
359 		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
360 			cn->cn_ops->cn_grab(cn);
361 	}
362 }
363 
364 void
365 cnungrab()
366 {
367 	struct cn_device *cnd;
368 	struct consdev *cn;
369 
370 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
371 		cn = cnd->cnd_cn;
372 		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
373 			cn->cn_ops->cn_ungrab(cn);
374 	}
375 }
376 
377 /*
378  * Low level console routines.
379  */
380 int
381 cngetc(void)
382 {
383 	int c;
384 
385 	if (cn_mute)
386 		return (-1);
387 	while ((c = cncheckc()) == -1)
388 		cpu_spinwait();
389 	if (c == '\r')
390 		c = '\n';		/* console input is always ICRNL */
391 	return (c);
392 }
393 
394 int
395 cncheckc(void)
396 {
397 	struct cn_device *cnd;
398 	struct consdev *cn;
399 	int c;
400 
401 	if (cn_mute)
402 		return (-1);
403 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
404 		cn = cnd->cnd_cn;
405 		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
406 			c = cn->cn_ops->cn_getc(cn);
407 			if (c != -1)
408 				return (c);
409 		}
410 	}
411 	return (-1);
412 }
413 
414 void
415 cngets(char *cp, size_t size, int visible)
416 {
417 	char *lp, *end;
418 	int c;
419 
420 	cngrab();
421 
422 	lp = cp;
423 	end = cp + size - 1;
424 	for (;;) {
425 		c = cngetc() & 0177;
426 		switch (c) {
427 		case '\n':
428 		case '\r':
429 			cnputc(c);
430 			*lp = '\0';
431 			cnungrab();
432 			return;
433 		case '\b':
434 		case '\177':
435 			if (lp > cp) {
436 				if (visible)
437 					cnputs("\b \b");
438 				lp--;
439 			}
440 			continue;
441 		case '\0':
442 			continue;
443 		default:
444 			if (lp < end) {
445 				switch (visible) {
446 				case GETS_NOECHO:
447 					break;
448 				case GETS_ECHOPASS:
449 					cnputc('*');
450 					break;
451 				default:
452 					cnputc(c);
453 					break;
454 				}
455 				*lp++ = c;
456 			}
457 		}
458 	}
459 }
460 
461 void
462 cnputc(int c)
463 {
464 	struct cn_device *cnd;
465 	struct consdev *cn;
466 	char *cp;
467 
468 #ifdef EARLY_PRINTF
469 	if (early_putc != NULL) {
470 		if (c == '\n')
471 			early_putc('\r');
472 		early_putc(c);
473 		return;
474 	}
475 #endif
476 
477 	if (cn_mute || c == '\0')
478 		return;
479 	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
480 		cn = cnd->cnd_cn;
481 		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
482 			if (c == '\n')
483 				cn->cn_ops->cn_putc(cn, '\r');
484 			cn->cn_ops->cn_putc(cn, c);
485 		}
486 	}
487 	if (console_pausing && c == '\n' && !kdb_active) {
488 		for (cp = console_pausestr; *cp != '\0'; cp++)
489 			cnputc(*cp);
490 		cngrab();
491 		if (cngetc() == '.')
492 			console_pausing = 0;
493 		cnungrab();
494 		cnputc('\r');
495 		for (cp = console_pausestr; *cp != '\0'; cp++)
496 			cnputc(' ');
497 		cnputc('\r');
498 	}
499 }
500 
501 void
502 cnputs(char *p)
503 {
504 	int c;
505 	int unlock_reqd = 0;
506 
507 	if (use_cnputs_mtx) {
508 		mtx_lock_spin(&cnputs_mtx);
509 		unlock_reqd = 1;
510 	}
511 
512 	while ((c = *p++) != '\0')
513 		cnputc(c);
514 
515 	if (unlock_reqd)
516 		mtx_unlock_spin(&cnputs_mtx);
517 }
518 
519 static int consmsgbuf_size = 8192;
520 SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
521     "Console tty buffer size");
522 
523 /*
524  * Redirect console output to a tty.
525  */
526 void
527 constty_set(struct tty *tp)
528 {
529 	int size;
530 
531 	KASSERT(tp != NULL, ("constty_set: NULL tp"));
532 	if (consbuf == NULL) {
533 		size = consmsgbuf_size;
534 		consbuf = malloc(size, M_TTYCONS, M_WAITOK);
535 		msgbuf_init(&consmsgbuf, consbuf, size);
536 		callout_init(&conscallout, 0);
537 	}
538 	constty = tp;
539 	constty_timeout(NULL);
540 }
541 
542 /*
543  * Disable console redirection to a tty.
544  */
545 void
546 constty_clear(void)
547 {
548 	int c;
549 
550 	constty = NULL;
551 	if (consbuf == NULL)
552 		return;
553 	callout_stop(&conscallout);
554 	while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
555 		cnputc(c);
556 	free(consbuf, M_TTYCONS);
557 	consbuf = NULL;
558 }
559 
560 /* Times per second to check for pending console tty messages. */
561 static int constty_wakeups_per_second = 5;
562 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
563     &constty_wakeups_per_second, 0,
564     "Times per second to check for pending console tty messages");
565 
566 static void
567 constty_timeout(void *arg)
568 {
569 	int c;
570 
571 	if (constty != NULL) {
572 		tty_lock(constty);
573 		while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
574 			if (tty_putchar(constty, c) < 0) {
575 				tty_unlock(constty);
576 				constty = NULL;
577 				break;
578 			}
579 		}
580 
581 		if (constty != NULL)
582 			tty_unlock(constty);
583 	}
584 	if (constty != NULL) {
585 		callout_reset(&conscallout, hz / constty_wakeups_per_second,
586 		    constty_timeout, NULL);
587 	} else {
588 		/* Deallocate the constty buffer memory. */
589 		constty_clear();
590 	}
591 }
592 
593 static void
594 cn_drvinit(void *unused)
595 {
596 
597 	mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
598 	use_cnputs_mtx = 1;
599 }
600 
601 SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
602 
603 /*
604  * Sysbeep(), if we have hardware for it
605  */
606 
607 #ifdef HAS_TIMER_SPKR
608 
609 static int beeping;
610 static struct callout beeping_timer;
611 
612 static void
613 sysbeepstop(void *chan)
614 {
615 
616 	timer_spkr_release();
617 	beeping = 0;
618 }
619 
620 int
621 sysbeep(int pitch, int period)
622 {
623 
624 	if (timer_spkr_acquire()) {
625 		if (!beeping) {
626 			/* Something else owns it. */
627 			return (EBUSY);
628 		}
629 	}
630 	timer_spkr_setfreq(pitch);
631 	if (!beeping) {
632 		beeping = period;
633 		callout_reset(&beeping_timer, period, sysbeepstop, NULL);
634 	}
635 	return (0);
636 }
637 
638 static void
639 sysbeep_init(void *unused)
640 {
641 
642 	callout_init(&beeping_timer, CALLOUT_MPSAFE);
643 }
644 SYSINIT(sysbeep, SI_SUB_SOFTINTR, SI_ORDER_ANY, sysbeep_init, NULL);
645 #else
646 
647 /*
648  * No hardware, no sound
649  */
650 
651 int
652 sysbeep(int pitch __unused, int period __unused)
653 {
654 
655 	return (ENODEV);
656 }
657 
658 #endif
659 
660 /*
661  * Temporary support for sc(4) to vt(4) transition.
662  */
663 static unsigned vty_prefer;
664 static char vty_name[16];
665 SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, vty_name,
666     0, "Console vty driver");
667 
668 int
669 vty_enabled(unsigned vty)
670 {
671 	static unsigned vty_selected = 0;
672 
673 	if (vty_selected == 0) {
674 		TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name));
675 		do {
676 #if defined(DEV_SC)
677 			if (strcmp(vty_name, "sc") == 0) {
678 				vty_selected = VTY_SC;
679 				break;
680 			}
681 #endif
682 #if defined(DEV_VT)
683 			if (strcmp(vty_name, "vt") == 0) {
684 				vty_selected = VTY_VT;
685 				break;
686 			}
687 #endif
688 			if (vty_prefer != 0) {
689 				vty_selected = vty_prefer;
690 				break;
691 			}
692 #if defined(DEV_VT)
693 			vty_selected = VTY_VT;
694 #elif defined(DEV_SC)
695 			vty_selected = VTY_SC;
696 #endif
697 		} while (0);
698 
699 		if (vty_selected == VTY_VT)
700 			strcpy(vty_name, "vt");
701 		else if (vty_selected == VTY_SC)
702 			strcpy(vty_name, "sc");
703 	}
704 	return ((vty_selected & vty) != 0);
705 }
706 
707 void
708 vty_set_preferred(unsigned vty)
709 {
710 
711 	vty_prefer = vty;
712 #if !defined(DEV_SC)
713 	vty_prefer &= ~VTY_SC;
714 #endif
715 #if !defined(DEV_VT)
716 	vty_prefer &= ~VTY_VT;
717 #endif
718 }
719 
720