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
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/lock.h>
48#include <sys/mutex.h>
49#include <sys/conf.h>
50#include <sys/cons.h>
51#include <sys/fcntl.h>
52#include <sys/kdb.h>
53#include <sys/kernel.h>
54#include <sys/malloc.h>
55#include <sys/msgbuf.h>
56#include <sys/namei.h>
57#include <sys/priv.h>
58#include <sys/proc.h>
59#include <sys/queue.h>
60#include <sys/reboot.h>
61#include <sys/sysctl.h>
62#include <sys/sbuf.h>
63#include <sys/tty.h>
64#include <sys/uio.h>
65#include <sys/vnode.h>
66
67#include <ddb/ddb.h>
68
69#include <machine/cpu.h>
70#include <machine/clock.h>
71
72static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
73
74struct cn_device {
75	STAILQ_ENTRY(cn_device) cnd_next;
76	struct		consdev *cnd_cn;
77};
78
79#define CNDEVPATHMAX	32
80#define CNDEVTAB_SIZE	4
81static struct cn_device cn_devtab[CNDEVTAB_SIZE];
82static STAILQ_HEAD(, cn_device) cn_devlist =
83    STAILQ_HEAD_INITIALIZER(cn_devlist);
84
85int	cons_avail_mask = 0;	/* Bit mask. Each registered low level console
86				 * which is currently unavailable for inpit
87				 * (i.e., if it is in graphics mode) will have
88				 * this bit cleared.
89				 */
90static int cn_mute;
91static char *consbuf;			/* buffer used by `consmsgbuf' */
92static struct callout conscallout;	/* callout for outputting to constty */
93struct msgbuf consmsgbuf;		/* message buffer for console tty */
94static u_char console_pausing;		/* pause after each line during probe */
95static char *console_pausestr=
96"<pause; press any key to proceed to next line or '.' to end pause mode>";
97struct tty *constty;			/* pointer to console "window" tty */
98static struct mtx cnputs_mtx;		/* Mutex for cnputs(). */
99static int use_cnputs_mtx = 0;		/* != 0 if cnputs_mtx locking reqd. */
100
101static void constty_timeout(void *arg);
102
103static struct consdev cons_consdev;
104DATA_SET(cons_set, cons_consdev);
105SET_DECLARE(cons_set, struct consdev);
106
107void
108cninit(void)
109{
110	struct consdev *best_cn, *cn, **list;
111
112	/*
113	 * Check if we should mute the console (for security reasons perhaps)
114	 * It can be changes dynamically using sysctl kern.consmute
115	 * once we are up and going.
116	 *
117	 */
118        cn_mute = ((boothowto & (RB_MUTE
119			|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