kern_cons.c revision 267654
19313Ssos/*-
29313Ssos * Copyright (c) 1988 University of Utah.
39313Ssos * Copyright (c) 1991 The Regents of the University of California.
49313Ssos * Copyright (c) 1999 Michael Smith
59313Ssos * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
69313Ssos *
79313Ssos * All rights reserved.
89313Ssos *
99313Ssos * This code is derived from software contributed to Berkeley by
109313Ssos * the Systems Programming Group of the University of Utah Computer
119313Ssos * Science Department.
129313Ssos *
139313Ssos * Redistribution and use in source and binary forms, with or without
149313Ssos * modification, are permitted provided that the following conditions
1597748Sschweikh * are met:
169313Ssos * 1. Redistributions of source code must retain the above copyright
179313Ssos *    notice, this list of conditions and the following disclaimer.
189313Ssos * 2. Redistributions in binary form must reproduce the above copyright
199313Ssos *    notice, this list of conditions and the following disclaimer in the
209313Ssos *    documentation and/or other materials provided with the distribution.
219313Ssos * 4. Neither the name of the University nor the names of its contributors
229313Ssos *    may be used to endorse or promote products derived from this software
239313Ssos *    without specific prior written permission.
249313Ssos *
259313Ssos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
269313Ssos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
279313Ssos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
289313Ssos * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29115705Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30115705Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31115705Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
329313Ssos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
339313Ssos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
349313Ssos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
359313Ssos * SUCH DAMAGE.
3614331Speter *
3768583Smarcel *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
3883221Smarcel */
3912458Sbde
4049850Smarcel#include <sys/cdefs.h>
4149850Smarcel__FBSDID("$FreeBSD: releng/9.3/sys/kern/kern_cons.c 241637 2012-10-17 11:30:14Z avg $");
4249850Smarcel
4383221Smarcel#include "opt_ddb.h"
4449850Smarcel
4549850Smarcel#include <sys/param.h>
4650345Smarcel#include <sys/systm.h>
4749850Smarcel#include <sys/lock.h>
4849850Smarcel#include <sys/mutex.h>
4949850Smarcel#include <sys/conf.h>
5049850Smarcel#include <sys/cons.h>
5149850Smarcel#include <sys/fcntl.h>
5249850Smarcel#include <sys/kdb.h>
5349850Smarcel#include <sys/kernel.h>
5449850Smarcel#include <sys/malloc.h>
5550345Smarcel#include <sys/msgbuf.h>
5650345Smarcel#include <sys/namei.h>
5750345Smarcel#include <sys/priv.h>
5850345Smarcel#include <sys/proc.h>
5950345Smarcel#include <sys/queue.h>
6050345Smarcel#include <sys/reboot.h>
6150345Smarcel#include <sys/sysctl.h>
62161310Snetchild#include <sys/sbuf.h>
6355784Smarcel#include <sys/tty.h>
6483221Smarcel#include <sys/uio.h>
6583221Smarcel#include <sys/vnode.h>
6683221Smarcel
6783221Smarcel#include <ddb/ddb.h>
68122802Ssobomax
69159799Snetchild#include <machine/cpu.h>
70159799Snetchild#include <machine/clock.h>
71159799Snetchild
72159799Snetchildstatic MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
73159799Snetchild
74159799Snetchildstruct cn_device {
75159799Snetchild	STAILQ_ENTRY(cn_device) cnd_next;
76159799Snetchild	struct		consdev *cnd_cn;
77159799Snetchild};
78159799Snetchild
79159799Snetchild#define CNDEVPATHMAX	32
80159799Snetchild#define CNDEVTAB_SIZE	4
81159799Snetchildstatic struct cn_device cn_devtab[CNDEVTAB_SIZE];
82159799Snetchildstatic STAILQ_HEAD(, cn_device) cn_devlist =
83159799Snetchild    STAILQ_HEAD_INITIALIZER(cn_devlist);
84159799Snetchild
85159799Snetchildint	cons_avail_mask = 0;	/* Bit mask. Each registered low level console
86159799Snetchild				 * which is currently unavailable for inpit
87159799Snetchild				 * (i.e., if it is in graphics mode) will have
88159799Snetchild				 * this bit cleared.
89159799Snetchild				 */
90159799Snetchildstatic int cn_mute;
91159799Snetchildstatic char *consbuf;			/* buffer used by `consmsgbuf' */
92159799Snetchildstatic struct callout conscallout;	/* callout for outputting to constty */
93178257Sjkimstruct msgbuf consmsgbuf;		/* message buffer for console tty */
94178257Sjkimstatic u_char console_pausing;		/* pause after each line during probe */
95178257Sjkimstatic char *console_pausestr=
96178257Sjkim"<pause; press any key to proceed to next line or '.' to end pause mode>";
97178257Sjkimstruct tty *constty;			/* pointer to console "window" tty */
98178257Sjkimstatic struct mtx cnputs_mtx;		/* Mutex for cnputs(). */
99122802Ssobomaxstatic int use_cnputs_mtx = 0;		/* != 0 if cnputs_mtx locking reqd. */
100122802Ssobomax
101122802Ssobomaxstatic void constty_timeout(void *arg);
102122802Ssobomax
103122802Ssobomaxstatic struct consdev cons_consdev;
104122802SsobomaxDATA_SET(cons_set, cons_consdev);
105122802SsobomaxSET_DECLARE(cons_set, struct consdev);
106122802Ssobomax
107122802Ssobomaxvoid
108122802Ssobomaxcninit(void)
109122802Ssobomax{
110122802Ssobomax	struct consdev *best_cn, *cn, **list;
111122802Ssobomax
112122802Ssobomax	/*
113122802Ssobomax	 * Check if we should mute the console (for security reasons perhaps)
114122802Ssobomax	 * It can be changes dynamically using sysctl kern.consmute
115122802Ssobomax	 * once we are up and going.
116122802Ssobomax	 *
117122802Ssobomax	 */
118122802Ssobomax        cn_mute = ((boothowto & (RB_MUTE
119122802Ssobomax			|RB_SINGLE
120			|RB_VERBOSE
121			|RB_ASKNAME)) == RB_MUTE);
122
123	/*
124	 * Find the first console with the highest priority.
125	 */
126	best_cn = NULL;
127	SET_FOREACH(list, cons_set) {
128		cn = *list;
129		cnremove(cn);
130		/* Skip cons_consdev. */
131		if (cn->cn_ops == NULL)
132			continue;
133		cn->cn_ops->cn_probe(cn);
134		if (cn->cn_pri == CN_DEAD)
135			continue;
136		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
137			best_cn = cn;
138		if (boothowto & RB_MULTIPLE) {
139			/*
140			 * Initialize console, and attach to it.
141			 */
142			cn->cn_ops->cn_init(cn);
143			cnadd(cn);
144		}
145	}
146	if (best_cn == NULL)
147		return;
148	if ((boothowto & RB_MULTIPLE) == 0) {
149		best_cn->cn_ops->cn_init(best_cn);
150		cnadd(best_cn);
151	}
152	if (boothowto & RB_PAUSE)
153		console_pausing = 1;
154	/*
155	 * Make the best console the preferred console.
156	 */
157	cnselect(best_cn);
158}
159
160void
161cninit_finish()
162{
163	console_pausing = 0;
164}
165
166/* add a new physical console to back the virtual console */
167int
168cnadd(struct consdev *cn)
169{
170	struct cn_device *cnd;
171	int i;
172
173	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
174		if (cnd->cnd_cn == cn)
175			return (0);
176	for (i = 0; i < CNDEVTAB_SIZE; i++) {
177		cnd = &cn_devtab[i];
178		if (cnd->cnd_cn == NULL)
179			break;
180	}
181	if (cnd->cnd_cn != NULL)
182		return (ENOMEM);
183	cnd->cnd_cn = cn;
184	if (cn->cn_name[0] == '\0') {
185		/* XXX: it is unclear if/where this print might output */
186		printf("WARNING: console at %p has no name\n", cn);
187	}
188	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
189	if (STAILQ_FIRST(&cn_devlist) == cnd)
190		ttyconsdev_select(cnd->cnd_cn->cn_name);
191
192	/* Add device to the active mask. */
193	cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
194
195	return (0);
196}
197
198void
199cnremove(struct consdev *cn)
200{
201	struct cn_device *cnd;
202	int i;
203
204	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
205		if (cnd->cnd_cn != cn)
206			continue;
207		if (STAILQ_FIRST(&cn_devlist) == cnd)
208			ttyconsdev_select(NULL);
209		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
210		cnd->cnd_cn = NULL;
211
212		/* Remove this device from available mask. */
213		for (i = 0; i < CNDEVTAB_SIZE; i++)
214			if (cnd == &cn_devtab[i]) {
215				cons_avail_mask &= ~(1 << i);
216				break;
217			}
218#if 0
219		/*
220		 * XXX
221		 * syscons gets really confused if console resources are
222		 * freed after the system has initialized.
223		 */
224		if (cn->cn_term != NULL)
225			cn->cn_ops->cn_term(cn);
226#endif
227		return;
228	}
229}
230
231void
232cnselect(struct consdev *cn)
233{
234	struct cn_device *cnd;
235
236	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
237		if (cnd->cnd_cn != cn)
238			continue;
239		if (cnd == STAILQ_FIRST(&cn_devlist))
240			return;
241		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
242		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
243		ttyconsdev_select(cnd->cnd_cn->cn_name);
244		return;
245	}
246}
247
248void
249cnavailable(struct consdev *cn, int available)
250{
251	int i;
252
253	for (i = 0; i < CNDEVTAB_SIZE; i++) {
254		if (cn_devtab[i].cnd_cn == cn)
255			break;
256	}
257	if (available) {
258		if (i < CNDEVTAB_SIZE)
259			cons_avail_mask |= (1 << i);
260		cn->cn_flags &= ~CN_FLAG_NOAVAIL;
261	} else {
262		if (i < CNDEVTAB_SIZE)
263			cons_avail_mask &= ~(1 << i);
264		cn->cn_flags |= CN_FLAG_NOAVAIL;
265	}
266}
267
268int
269cnunavailable(void)
270{
271
272	return (cons_avail_mask == 0);
273}
274
275/*
276 * sysctl_kern_console() provides output parseable in conscontrol(1).
277 */
278static int
279sysctl_kern_console(SYSCTL_HANDLER_ARGS)
280{
281	struct cn_device *cnd;
282	struct consdev *cp, **list;
283	char *p;
284	int delete, error;
285	struct sbuf *sb;
286
287	sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND);
288	if (sb == NULL)
289		return (ENOMEM);
290	sbuf_clear(sb);
291	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
292		sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name);
293	sbuf_printf(sb, "/");
294	SET_FOREACH(list, cons_set) {
295		cp = *list;
296		if (cp->cn_name[0] != '\0')
297			sbuf_printf(sb, "%s,", cp->cn_name);
298	}
299	sbuf_finish(sb);
300	error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
301	if (error == 0 && req->newptr != NULL) {
302		p = sbuf_data(sb);
303		error = ENXIO;
304		delete = 0;
305		if (*p == '-') {
306			delete = 1;
307			p++;
308		}
309		SET_FOREACH(list, cons_set) {
310			cp = *list;
311			if (strcmp(p, cp->cn_name) != 0)
312				continue;
313			if (delete) {
314				cnremove(cp);
315				error = 0;
316			} else {
317				error = cnadd(cp);
318				if (error == 0)
319					cnselect(cp);
320			}
321			break;
322		}
323	}
324	sbuf_delete(sb);
325	return (error);
326}
327
328SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
329	0, 0, sysctl_kern_console, "A", "Console device control");
330
331/*
332 * User has changed the state of the console muting.
333 * This may require us to open or close the device in question.
334 */
335static int
336sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
337{
338	int error;
339
340	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
341	if (error != 0 || req->newptr == NULL)
342		return (error);
343	return (error);
344}
345
346SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
347	0, sizeof(cn_mute), sysctl_kern_consmute, "I",
348	"State of the console muting");
349
350void
351cngrab()
352{
353	struct cn_device *cnd;
354	struct consdev *cn;
355
356	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
357		cn = cnd->cnd_cn;
358		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
359			cn->cn_ops->cn_grab(cn);
360	}
361}
362
363void
364cnungrab()
365{
366	struct cn_device *cnd;
367	struct consdev *cn;
368
369	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
370		cn = cnd->cnd_cn;
371		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
372			cn->cn_ops->cn_ungrab(cn);
373	}
374}
375
376/*
377 * Low level console routines.
378 */
379int
380cngetc(void)
381{
382	int c;
383
384	if (cn_mute)
385		return (-1);
386	while ((c = cncheckc()) == -1)
387		cpu_spinwait();
388	if (c == '\r')
389		c = '\n';		/* console input is always ICRNL */
390	return (c);
391}
392
393int
394cncheckc(void)
395{
396	struct cn_device *cnd;
397	struct consdev *cn;
398	int c;
399
400	if (cn_mute)
401		return (-1);
402	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
403		cn = cnd->cnd_cn;
404		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
405			c = cn->cn_ops->cn_getc(cn);
406			if (c != -1)
407				return (c);
408		}
409	}
410	return (-1);
411}
412
413void
414cngets(char *cp, size_t size, int visible)
415{
416	char *lp, *end;
417	int c;
418
419	cngrab();
420
421	lp = cp;
422	end = cp + size - 1;
423	for (;;) {
424		c = cngetc() & 0177;
425		switch (c) {
426		case '\n':
427		case '\r':
428			cnputc(c);
429			*lp = '\0';
430			cnungrab();
431			return;
432		case '\b':
433		case '\177':
434			if (lp > cp) {
435				if (visible) {
436					cnputc(c);
437					cnputs(" \b");
438				}
439				lp--;
440			}
441			continue;
442		case '\0':
443			continue;
444		default:
445			if (lp < end) {
446				switch (visible) {
447				case GETS_NOECHO:
448					break;
449				case GETS_ECHOPASS:
450					cnputc('*');
451					break;
452				default:
453					cnputc(c);
454					break;
455				}
456				*lp++ = c;
457			}
458		}
459	}
460}
461
462void
463cnputc(int c)
464{
465	struct cn_device *cnd;
466	struct consdev *cn;
467	char *cp;
468
469	if (cn_mute || c == '\0')
470		return;
471	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
472		cn = cnd->cnd_cn;
473		if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
474			if (c == '\n')
475				cn->cn_ops->cn_putc(cn, '\r');
476			cn->cn_ops->cn_putc(cn, c);
477		}
478	}
479	if (console_pausing && c == '\n' && !kdb_active) {
480		for (cp = console_pausestr; *cp != '\0'; cp++)
481			cnputc(*cp);
482		cngrab();
483		if (cngetc() == '.')
484			console_pausing = 0;
485		cnungrab();
486		cnputc('\r');
487		for (cp = console_pausestr; *cp != '\0'; cp++)
488			cnputc(' ');
489		cnputc('\r');
490	}
491}
492
493void
494cnputs(char *p)
495{
496	int c;
497	int unlock_reqd = 0;
498
499	if (use_cnputs_mtx) {
500		mtx_lock_spin(&cnputs_mtx);
501		unlock_reqd = 1;
502	}
503
504	while ((c = *p++) != '\0')
505		cnputc(c);
506
507	if (unlock_reqd)
508		mtx_unlock_spin(&cnputs_mtx);
509}
510
511static int consmsgbuf_size = 8192;
512SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
513    "Console tty buffer size");
514
515/*
516 * Redirect console output to a tty.
517 */
518void
519constty_set(struct tty *tp)
520{
521	int size;
522
523	KASSERT(tp != NULL, ("constty_set: NULL tp"));
524	if (consbuf == NULL) {
525		size = consmsgbuf_size;
526		consbuf = malloc(size, M_TTYCONS, M_WAITOK);
527		msgbuf_init(&consmsgbuf, consbuf, size);
528		callout_init(&conscallout, 0);
529	}
530	constty = tp;
531	constty_timeout(NULL);
532}
533
534/*
535 * Disable console redirection to a tty.
536 */
537void
538constty_clear(void)
539{
540	int c;
541
542	constty = NULL;
543	if (consbuf == NULL)
544		return;
545	callout_stop(&conscallout);
546	while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
547		cnputc(c);
548	free(consbuf, M_TTYCONS);
549	consbuf = NULL;
550}
551
552/* Times per second to check for pending console tty messages. */
553static int constty_wakeups_per_second = 5;
554SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
555    &constty_wakeups_per_second, 0,
556    "Times per second to check for pending console tty messages");
557
558static void
559constty_timeout(void *arg)
560{
561	int c;
562
563	if (constty != NULL) {
564		tty_lock(constty);
565		while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
566			if (tty_putchar(constty, c) < 0) {
567				tty_unlock(constty);
568				constty = NULL;
569				break;
570			}
571		}
572
573		if (constty != NULL)
574			tty_unlock(constty);
575	}
576	if (constty != NULL) {
577		callout_reset(&conscallout, hz / constty_wakeups_per_second,
578		    constty_timeout, NULL);
579	} else {
580		/* Deallocate the constty buffer memory. */
581		constty_clear();
582	}
583}
584
585static void
586cn_drvinit(void *unused)
587{
588
589	mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
590	use_cnputs_mtx = 1;
591}
592
593SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
594
595/*
596 * Sysbeep(), if we have hardware for it
597 */
598
599#ifdef HAS_TIMER_SPKR
600
601static int beeping;
602
603static void
604sysbeepstop(void *chan)
605{
606
607	timer_spkr_release();
608	beeping = 0;
609}
610
611int
612sysbeep(int pitch, int period)
613{
614
615	if (timer_spkr_acquire()) {
616		if (!beeping) {
617			/* Something else owns it. */
618			return (EBUSY);
619		}
620	}
621	timer_spkr_setfreq(pitch);
622	if (!beeping) {
623		beeping = period;
624		timeout(sysbeepstop, (void *)NULL, period);
625	}
626	return (0);
627}
628
629#else
630
631/*
632 * No hardware, no sound
633 */
634
635int
636sysbeep(int pitch __unused, int period __unused)
637{
638
639	return (ENODEV);
640}
641
642#endif
643
644