xref: /illumos-gate/usr/src/uts/i86pc/boot/boot_console.c (revision 33c72b7598992897b94815b1f47b7b8077e53808)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2012 Gary Mills
23  *
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/systm.h>
30 #include <sys/archsystm.h>
31 #include <sys/framebuffer.h>
32 #include <sys/boot_console.h>
33 #include <sys/panic.h>
34 #include <sys/ctype.h>
35 #include <sys/ascii.h>
36 #include <sys/vgareg.h>
37 #if defined(__xpv)
38 #include <sys/hypervisor.h>
39 #endif /* __xpv */
40 
41 #include "boot_console_impl.h"
42 #include "boot_serial.h"
43 
44 #if defined(_BOOT)
45 #include <dboot/dboot_asm.h>
46 #include <dboot/dboot_xboot.h>
47 #else /* _BOOT */
48 #include <sys/bootconf.h>
49 #if defined(__xpv)
50 #include <sys/evtchn_impl.h>
51 #endif /* __xpv */
52 static char *defcons_buf;
53 static char *defcons_cur;
54 #endif /* _BOOT */
55 
56 #if defined(__xpv)
57 extern void bcons_init_xen(char *);
58 extern void bcons_putchar_xen(int);
59 extern int bcons_getchar_xen(void);
60 extern int bcons_ischar_xen(void);
61 #endif /* __xpv */
62 
63 fb_info_t fb_info;
64 static bcons_dev_t bcons_dev;				/* Device callbacks */
65 static int console = CONS_SCREEN_TEXT;
66 static int diag = CONS_INVALID;
67 static int tty_num = 0;
68 static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
69 static char *boot_line;
70 static struct boot_env {
71 	char	*be_env;	/* ends with double ascii nul */
72 	size_t	be_size;	/* size of the environment, including nul */
73 } boot_env;
74 
75 /*
76  * Simple console terminal emulator for early boot.
77  * We need this to support kmdb, all other console output is supposed
78  * to be simple text output.
79  */
80 typedef enum btem_state_type {
81 	A_STATE_START,
82 	A_STATE_ESC,
83 	A_STATE_CSI,
84 	A_STATE_CSI_QMARK,
85 	A_STATE_CSI_EQUAL
86 } btem_state_type_t;
87 
88 #define	BTEM_MAXPARAMS	5
89 typedef struct btem_state {
90 	btem_state_type_t btem_state;
91 	boolean_t btem_gotparam;
92 	int btem_curparam;
93 	int btem_paramval;
94 	int btem_params[BTEM_MAXPARAMS];
95 } btem_state_t;
96 
97 static btem_state_t boot_tem;
98 
99 static int serial_ischar(void);
100 static int serial_getchar(void);
101 static void serial_putchar(int);
102 static void serial_adjust_prop(void);
103 
104 #if !defined(_BOOT)
105 /* Set if the console or mode are expressed in the boot line */
106 static int console_set, console_mode_set;
107 #endif
108 
109 #if defined(__xpv)
110 static int console_hypervisor_redirect = B_FALSE;
111 static int console_hypervisor_device = CONS_INVALID;
112 static int console_hypervisor_tty_num = 0;
113 
114 /* Obtain the hypervisor console type */
115 int
116 console_hypervisor_dev_type(int *tnum)
117 {
118 	if (tnum != NULL)
119 		*tnum = console_hypervisor_tty_num;
120 	return (console_hypervisor_device);
121 }
122 #endif /* __xpv */
123 
124 static int port;
125 
126 static void
127 serial_init(void)
128 {
129 	port = tty_addr[tty_num];
130 
131 	outb(port + ISR, 0x20);
132 	if (inb(port + ISR) & 0x20) {
133 		/*
134 		 * 82510 chip is present
135 		 */
136 		outb(port + DAT+7, 0x04);	/* clear status */
137 		outb(port + ISR, 0x40);  /* set to bank 2 */
138 		outb(port + MCR, 0x08);  /* IMD */
139 		outb(port + DAT, 0x21);  /* FMD */
140 		outb(port + ISR, 0x00);  /* set to bank 0 */
141 	} else {
142 		/*
143 		 * set the UART in FIFO mode if it has FIFO buffers.
144 		 * use 16550 fifo reset sequence specified in NS
145 		 * application note. disable fifos until chip is
146 		 * initialized.
147 		 */
148 		outb(port + FIFOR, 0x00);		/* clear */
149 		outb(port + FIFOR, FIFO_ON);		/* enable */
150 		outb(port + FIFOR, FIFO_ON|FIFORXFLSH);  /* reset */
151 		outb(port + FIFOR,
152 		    FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
153 		if ((inb(port + ISR) & 0xc0) != 0xc0) {
154 			/*
155 			 * no fifo buffers so disable fifos.
156 			 * this is true for 8250's
157 			 */
158 			outb(port + FIFOR, 0x00);
159 		}
160 	}
161 
162 	/* disable interrupts */
163 	outb(port + ICR, 0);
164 
165 #if !defined(_BOOT)
166 	if (IN_XPV_PANIC())
167 		return;
168 #endif
169 
170 	/* adjust setting based on tty properties */
171 	serial_adjust_prop();
172 }
173 
174 /* Advance str pointer past white space */
175 #define	EAT_WHITE_SPACE(str)	{			\
176 	while ((*str != '\0') && ISSPACE(*str))		\
177 		str++;					\
178 }
179 
180 /*
181  * boot_line is set when we call here.  Search it for the argument name,
182  * and if found, return a pointer to it.
183  */
184 static char *
185 find_boot_line_prop(const char *name)
186 {
187 	char *ptr;
188 	char *ret = NULL;
189 	char end_char;
190 	size_t len;
191 
192 	if (boot_line == NULL)
193 		return (NULL);
194 
195 	len = strlen(name);
196 
197 	/*
198 	 * We have two nested loops here: the outer loop discards all options
199 	 * except -B, and the inner loop parses the -B options looking for
200 	 * the one we're interested in.
201 	 */
202 	for (ptr = boot_line; *ptr != '\0'; ptr++) {
203 		EAT_WHITE_SPACE(ptr);
204 
205 		if (*ptr == '-') {
206 			ptr++;
207 			while ((*ptr != '\0') && (*ptr != 'B') &&
208 			    !ISSPACE(*ptr))
209 				ptr++;
210 			if (*ptr == '\0')
211 				goto out;
212 			else if (*ptr != 'B')
213 				continue;
214 		} else {
215 			while ((*ptr != '\0') && !ISSPACE(*ptr))
216 				ptr++;
217 			if (*ptr == '\0')
218 				goto out;
219 			continue;
220 		}
221 
222 		do {
223 			ptr++;
224 			EAT_WHITE_SPACE(ptr);
225 
226 			if ((strncmp(ptr, name, len) == 0) &&
227 			    (ptr[len] == '=')) {
228 				ptr += len + 1;
229 				if ((*ptr == '\'') || (*ptr == '"')) {
230 					ret = ptr + 1;
231 					end_char = *ptr;
232 					ptr++;
233 				} else {
234 					ret = ptr;
235 					end_char = ',';
236 				}
237 				goto consume_property;
238 			}
239 
240 			/*
241 			 * We have a property, and it's not the one we're
242 			 * interested in.  Skip the property name.  A name
243 			 * can end with '=', a comma, or white space.
244 			 */
245 			while ((*ptr != '\0') && (*ptr != '=') &&
246 			    (*ptr != ',') && (!ISSPACE(*ptr)))
247 				ptr++;
248 
249 			/*
250 			 * We only want to go through the rest of the inner
251 			 * loop if we have a comma.  If we have a property
252 			 * name without a value, either continue or break.
253 			 */
254 			if (*ptr == '\0')
255 				goto out;
256 			else if (*ptr == ',')
257 				continue;
258 			else if (ISSPACE(*ptr))
259 				break;
260 			ptr++;
261 
262 			/*
263 			 * Is the property quoted?
264 			 */
265 			if ((*ptr == '\'') || (*ptr == '"')) {
266 				end_char = *ptr;
267 				ptr++;
268 			} else {
269 				/*
270 				 * Not quoted, so the string ends at a comma
271 				 * or at white space.  Deal with white space
272 				 * later.
273 				 */
274 				end_char = ',';
275 			}
276 
277 			/*
278 			 * Now, we can ignore any characters until we find
279 			 * end_char.
280 			 */
281 consume_property:
282 			for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
283 				if ((end_char == ',') && ISSPACE(*ptr))
284 					break;
285 			}
286 			if (*ptr && (*ptr != ',') && !ISSPACE(*ptr))
287 				ptr++;
288 		} while (*ptr == ',');
289 	}
290 out:
291 	return (ret);
292 }
293 
294 /*
295  * Find prop from boot env module. The data in module is list of C strings
296  * name=value, the list is terminated by double nul.
297  */
298 static const char *
299 find_boot_env_prop(const char *name)
300 {
301 	char *ptr;
302 	size_t len;
303 	uintptr_t size;
304 
305 	if (boot_env.be_env == NULL)
306 		return (NULL);
307 
308 	ptr = boot_env.be_env;
309 	len = strlen(name);
310 
311 	/*
312 	 * Make sure we have at least len + 2 bytes in the environment.
313 	 * We are looking for name=value\0 constructs, and the environment
314 	 * itself is terminated by '\0'.
315 	 */
316 	if (boot_env.be_size < len + 2)
317 		return (NULL);
318 
319 	do {
320 		if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) {
321 			ptr += len + 1;
322 			return (ptr);
323 		}
324 		/* find the first '\0' */
325 		while (*ptr != '\0') {
326 			ptr++;
327 			size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
328 			if (size > boot_env.be_size)
329 				return (NULL);
330 		}
331 		ptr++;
332 
333 		/* If the remainder is shorter than name + 2, get out. */
334 		size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
335 		if (boot_env.be_size - size < len + 2)
336 			return (NULL);
337 	} while (*ptr != '\0');
338 	return (NULL);
339 }
340 
341 /*
342  * Get prop value from either command line or boot environment.
343  * We always check kernel command line first, as this will keep the
344  * functionality and will allow user to override the values in environment.
345  */
346 const char *
347 find_boot_prop(const char *name)
348 {
349 	const char *value = find_boot_line_prop(name);
350 
351 	if (value == NULL)
352 		value = find_boot_env_prop(name);
353 	return (value);
354 }
355 
356 #define	MATCHES(p, pat)	\
357 	(strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
358 
359 #define	SKIP(p, c)				\
360 	while (*(p) != 0 && *p != (c))		\
361 		++(p);				\
362 	if (*(p) == (c))			\
363 		++(p);
364 
365 /*
366  * find a tty mode property either from cmdline or from boot properties
367  */
368 static const char *
369 get_mode_value(char *name)
370 {
371 	/*
372 	 * when specified on boot line it looks like "name" "="....
373 	 */
374 	if (boot_line != NULL) {
375 		return (find_boot_prop(name));
376 	}
377 
378 #if defined(_BOOT)
379 	return (NULL);
380 #else
381 	/*
382 	 * if we're running in the full kernel we check the bootenv.rc settings
383 	 */
384 	{
385 		static char propval[20];
386 
387 		propval[0] = 0;
388 		if (do_bsys_getproplen(NULL, name) <= 0)
389 			return (NULL);
390 		(void) do_bsys_getprop(NULL, name, propval);
391 		return (propval);
392 	}
393 #endif
394 }
395 
396 /*
397  * adjust serial port based on properties
398  * These come either from the cmdline or from boot properties.
399  */
400 static void
401 serial_adjust_prop(void)
402 {
403 	char propname[20];
404 	const char *propval;
405 	const char *p;
406 	ulong_t baud;
407 	uchar_t lcr = 0;
408 	uchar_t mcr = DTR | RTS;
409 
410 	(void) strcpy(propname, "ttyX-mode");
411 	propname[3] = 'a' + tty_num;
412 	propval = get_mode_value(propname);
413 	if (propval == NULL)
414 		propval = "9600,8,n,1,-";
415 #if !defined(_BOOT)
416 	else
417 		console_mode_set = 1;
418 #endif
419 
420 	/* property is of the form: "9600,8,n,1,-" */
421 	p = propval;
422 	if (MATCHES(p, "110,"))
423 		baud = ASY110;
424 	else if (MATCHES(p, "150,"))
425 		baud = ASY150;
426 	else if (MATCHES(p, "300,"))
427 		baud = ASY300;
428 	else if (MATCHES(p, "600,"))
429 		baud = ASY600;
430 	else if (MATCHES(p, "1200,"))
431 		baud = ASY1200;
432 	else if (MATCHES(p, "2400,"))
433 		baud = ASY2400;
434 	else if (MATCHES(p, "4800,"))
435 		baud = ASY4800;
436 	else if (MATCHES(p, "19200,"))
437 		baud = ASY19200;
438 	else if (MATCHES(p, "38400,"))
439 		baud = ASY38400;
440 	else if (MATCHES(p, "57600,"))
441 		baud = ASY57600;
442 	else if (MATCHES(p, "115200,"))
443 		baud = ASY115200;
444 	else {
445 		baud = ASY9600;
446 		SKIP(p, ',');
447 	}
448 	outb(port + LCR, DLAB);
449 	outb(port + DAT + DLL, baud & 0xff);
450 	outb(port + DAT + DLH, (baud >> 8) & 0xff);
451 
452 	switch (*p) {
453 	case '5':
454 		lcr |= BITS5;
455 		++p;
456 		break;
457 	case '6':
458 		lcr |= BITS6;
459 		++p;
460 		break;
461 	case '7':
462 		lcr |= BITS7;
463 		++p;
464 		break;
465 	case '8':
466 		++p;
467 		/* FALLTHROUGH */
468 	default:
469 		lcr |= BITS8;
470 		break;
471 	}
472 
473 	SKIP(p, ',');
474 
475 	switch (*p) {
476 	case 'n':
477 		lcr |= PARITY_NONE;
478 		++p;
479 		break;
480 	case 'o':
481 		lcr |= PARITY_ODD;
482 		++p;
483 		break;
484 	case 'e':
485 		++p;
486 		/* FALLTHROUGH */
487 	default:
488 		lcr |= PARITY_EVEN;
489 		break;
490 	}
491 
492 
493 	SKIP(p, ',');
494 
495 	switch (*p) {
496 	case '1':
497 		/* STOP1 is 0 */
498 		++p;
499 		break;
500 	default:
501 		lcr |= STOP2;
502 		break;
503 	}
504 	/* set parity bits */
505 	outb(port + LCR, lcr);
506 
507 	(void) strcpy(propname, "ttyX-rts-dtr-off");
508 	propname[3] = 'a' + tty_num;
509 	propval = get_mode_value(propname);
510 	if (propval == NULL)
511 		propval = "false";
512 	if (propval[0] != 'f' && propval[0] != 'F')
513 		mcr = 0;
514 	/* set modem control bits */
515 	outb(port + MCR, mcr | OUT2);
516 }
517 
518 /* Obtain the console type */
519 int
520 boot_console_type(int *tnum)
521 {
522 	if (tnum != NULL)
523 		*tnum = tty_num;
524 	return (console);
525 }
526 
527 /*
528  * A structure to map console names to values.
529  */
530 typedef struct {
531 	char *name;
532 	int value;
533 } console_value_t;
534 
535 console_value_t console_devices[] = {
536 	{ "ttya", CONS_TTY },	/* 0 */
537 	{ "ttyb", CONS_TTY },	/* 1 */
538 	{ "ttyc", CONS_TTY },	/* 2 */
539 	{ "ttyd", CONS_TTY },	/* 3 */
540 	{ "text", CONS_SCREEN_TEXT },
541 	{ "graphics", CONS_SCREEN_GRAPHICS },
542 #if defined(__xpv)
543 	{ "hypervisor", CONS_HYPERVISOR },
544 #endif
545 #if !defined(_BOOT)
546 	{ "usb-serial", CONS_USBSER },
547 #endif
548 	{ NULL, CONS_INVALID }
549 };
550 
551 static void
552 bcons_init_env(struct xboot_info *xbi)
553 {
554 	uint32_t i;
555 	struct boot_modules *modules;
556 
557 	modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules;
558 	for (i = 0; i < xbi->bi_module_cnt; i++) {
559 		if (modules[i].bm_type == BMT_ENV)
560 			break;
561 	}
562 	if (i == xbi->bi_module_cnt)
563 		return;
564 
565 	boot_env.be_env = (char *)(uintptr_t)modules[i].bm_addr;
566 	boot_env.be_size = modules[i].bm_size;
567 }
568 
569 int
570 boot_fb(struct xboot_info *xbi, int console)
571 {
572 	if (xbi_fb_init(xbi, &bcons_dev) == B_FALSE)
573 		return (console);
574 
575 	/* FB address is not set, fall back to serial terminal. */
576 	if (fb_info.paddr == 0)
577 		return (CONS_TTY);
578 
579 	fb_info.terminal.x = VGA_TEXT_COLS;
580 	fb_info.terminal.y = VGA_TEXT_ROWS;
581 	boot_fb_init(CONS_FRAMEBUFFER);
582 
583 	if (console == CONS_SCREEN_TEXT)
584 		return (CONS_FRAMEBUFFER);
585 	return (console);
586 }
587 
588 /*
589  * TODO.
590  * quick and dirty local atoi. Perhaps should build with strtol, but
591  * dboot & early boot mix does overcomplicate things much.
592  * Stolen from libc anyhow.
593  */
594 static int
595 atoi(const char *p)
596 {
597 	int n, c, neg = 0;
598 	unsigned char *up = (unsigned char *)p;
599 
600 	if (!isdigit(c = *up)) {
601 		while (isspace(c))
602 			c = *++up;
603 		switch (c) {
604 		case '-':
605 			neg++;
606 			/* FALLTHROUGH */
607 		case '+':
608 			c = *++up;
609 		}
610 		if (!isdigit(c))
611 			return (0);
612 	}
613 	for (n = '0' - c; isdigit(c = *++up); ) {
614 		n *= 10; /* two steps to avoid unnecessary overflow */
615 		n += '0' - c; /* accum neg to avoid surprises at MAX */
616 	}
617 	return (neg ? n : -n);
618 }
619 
620 static void
621 bcons_init_fb(void)
622 {
623 	const char *propval;
624 	int intval;
625 
626 	/* initialize with explicit default values */
627 	fb_info.fg_color = CONS_COLOR;
628 	fb_info.bg_color = 0;
629 	fb_info.inverse = B_FALSE;
630 	fb_info.inverse_screen = B_FALSE;
631 
632 	/* color values are 0 - 255 */
633 	propval = find_boot_prop("tem.fg_color");
634 	if (propval != NULL) {
635 		intval = atoi(propval);
636 		if (intval >= 0 && intval <= 255)
637 			fb_info.fg_color = intval;
638 	}
639 
640 	/* color values are 0 - 255 */
641 	propval = find_boot_prop("tem.bg_color");
642 	if (propval != NULL && ISDIGIT(*propval)) {
643 		intval = atoi(propval);
644 		if (intval >= 0 && intval <= 255)
645 			fb_info.bg_color = intval;
646 	}
647 
648 	/* get inverses. allow 0, 1, true, false */
649 	propval = find_boot_prop("tem.inverse");
650 	if (propval != NULL) {
651 		if (*propval == '1' || MATCHES(propval, "true"))
652 			fb_info.inverse = B_TRUE;
653 	}
654 
655 	propval = find_boot_prop("tem.inverse-screen");
656 	if (propval != NULL) {
657 		if (*propval == '1' || MATCHES(propval, "true"))
658 			fb_info.inverse_screen = B_TRUE;
659 	}
660 
661 #if defined(_BOOT)
662 	/*
663 	 * Load cursor position from bootloader only in dboot,
664 	 * dboot will pass cursor position to kernel via xboot info.
665 	 */
666 	propval = find_boot_prop("tem.cursor.row");
667 	if (propval != NULL) {
668 		intval = atoi(propval);
669 		if (intval >= 0 && intval <= 0xFFFF)
670 			fb_info.cursor.pos.y = intval;
671 	}
672 
673 	propval = find_boot_prop("tem.cursor.col");
674 	if (propval != NULL) {
675 		intval = atoi(propval);
676 		if (intval >= 0 && intval <= 0xFFFF)
677 			fb_info.cursor.pos.x = intval;
678 	}
679 #endif
680 }
681 
682 /*
683  * Go through the console_devices array trying to match the string
684  * we were given.  The string on the command line must end with
685  * a comma or white space.
686  *
687  * This function does set tty_num as an side effect, this does imply that
688  * only one of the main console and the diag-device can be using serial.
689  */
690 static int
691 lookup_console_devices(const char *cons_str)
692 {
693 	int n, cons;
694 	size_t len, cons_len;
695 	console_value_t *consolep;
696 
697 	cons = CONS_INVALID;
698 	if (cons_str != NULL) {
699 
700 		cons_len = strlen(cons_str);
701 		for (n = 0; console_devices[n].name != NULL; n++) {
702 			consolep = &console_devices[n];
703 			len = strlen(consolep->name);
704 			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
705 			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
706 			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
707 			    (strncmp(cons_str, consolep->name, len) == 0)) {
708 				cons = consolep->value;
709 				if (cons == CONS_TTY)
710 					tty_num = n;
711 				break;
712 			}
713 		}
714 	}
715 	return (cons);
716 }
717 
718 void
719 bcons_init(struct xboot_info *xbi)
720 {
721 	const char *cons_str;
722 #if !defined(_BOOT)
723 	static char console_text[] = "text";
724 	extern int post_fastreboot;
725 #endif
726 
727 	if (xbi == NULL) {
728 		/* This is very early dboot console, set up ttya. */
729 		console = CONS_TTY;
730 		serial_init();
731 		return;
732 	}
733 
734 	/* Set up data to fetch properties from commad line and boot env. */
735 	boot_line = (char *)(uintptr_t)xbi->bi_cmdline;
736 	bcons_init_env(xbi);
737 	console = CONS_INVALID;
738 
739 	/* set up initial fb_info */
740 	bcons_init_fb();
741 
742 #if defined(__xpv)
743 	bcons_init_xen(boot_line);
744 #endif /* __xpv */
745 
746 	/*
747 	 * First check for diag-device.
748 	 */
749 	cons_str = find_boot_prop("diag-device");
750 	if (cons_str != NULL)
751 		diag = lookup_console_devices(cons_str);
752 
753 	cons_str = find_boot_prop("console");
754 	if (cons_str == NULL)
755 		cons_str = find_boot_prop("output-device");
756 
757 #if !defined(_BOOT)
758 	if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
759 		cons_str = console_text;
760 #endif
761 
762 	if (cons_str != NULL)
763 		console = lookup_console_devices(cons_str);
764 
765 #if defined(__xpv)
766 	/*
767 	 * domU's always use the hypervisor regardless of what
768 	 * the console variable may be set to.
769 	 */
770 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
771 		console = CONS_HYPERVISOR;
772 		console_hypervisor_redirect = B_TRUE;
773 	}
774 #endif /* __xpv */
775 
776 	/*
777 	 * If no console device specified, default to text.
778 	 * Remember what was specified for second phase.
779 	 */
780 	if (console == CONS_INVALID)
781 		console = CONS_SCREEN_TEXT;
782 #if !defined(_BOOT)
783 	else
784 		console_set = 1;
785 #endif
786 
787 #if defined(__xpv)
788 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
789 		switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
790 			case XEN_CONSOLE_COM1:
791 			case XEN_CONSOLE_COM2:
792 				console_hypervisor_device = CONS_TTY;
793 				console_hypervisor_tty_num = tty_num;
794 				break;
795 			case XEN_CONSOLE_VGA:
796 				/*
797 				 * Currently xen doesn't really support
798 				 * keyboard/display console devices.
799 				 * What this setting means is that
800 				 * "vga=keep" has been enabled, which is
801 				 * more of a xen debugging tool that a
802 				 * true console mode.  Hence, we're going
803 				 * to ignore this xen "console" setting.
804 				 */
805 				/*FALLTHROUGH*/
806 			default:
807 				console_hypervisor_device = CONS_INVALID;
808 		}
809 	}
810 
811 	/*
812 	 * if the hypervisor is using the currently selected serial
813 	 * port then default to using the hypervisor as the console
814 	 * device.
815 	 */
816 	if (console == console_hypervisor_device) {
817 		console = CONS_HYPERVISOR;
818 		console_hypervisor_redirect = B_TRUE;
819 	}
820 #endif /* __xpv */
821 
822 	/* make sure the FB is set up if present */
823 	console = boot_fb(xbi, console);
824 	switch (console) {
825 	case CONS_TTY:
826 		serial_init();
827 		break;
828 
829 	case CONS_HYPERVISOR:
830 		break;
831 
832 #if !defined(_BOOT)
833 	case CONS_USBSER:
834 		/*
835 		 * We can't do anything with the usb serial
836 		 * until we have memory management.
837 		 */
838 		break;
839 #endif
840 	case CONS_SCREEN_GRAPHICS:
841 		kb_init();
842 		break;
843 	case CONS_SCREEN_TEXT:
844 		boot_vga_init(&bcons_dev);
845 		/* Fall through */
846 	default:
847 		kb_init();
848 		break;
849 	}
850 
851 	/*
852 	 * Initialize diag device unless already done.
853 	 */
854 	switch (diag) {
855 	case CONS_TTY:
856 		if (console != CONS_TTY)
857 			serial_init();
858 		break;
859 	case CONS_SCREEN_GRAPHICS:
860 	case CONS_SCREEN_TEXT:
861 		if (console != CONS_SCREEN_GRAPHICS &&
862 		    console != CONS_SCREEN_TEXT)
863 			kb_init();
864 		break;
865 	default:
866 		break;
867 	}
868 }
869 
870 #if !defined(_BOOT)
871 /*
872  * 2nd part of console initialization.
873  * In the kernel (ie. fakebop), this can be used only to switch to
874  * using a serial port instead of screen based on the contents
875  * of the bootenv.rc file.
876  */
877 /*ARGSUSED*/
878 void
879 bcons_init2(char *inputdev, char *outputdev, char *consoledev)
880 {
881 	int cons = CONS_INVALID;
882 	int ttyn;
883 	char *devnames[] = { consoledev, outputdev, inputdev, NULL };
884 	console_value_t *consolep;
885 	int i;
886 	extern int post_fastreboot;
887 
888 	if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
889 		console = CONS_SCREEN_TEXT;
890 
891 	if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
892 		if (console_set) {
893 			/*
894 			 * If the console was set on the command line,
895 			 * but the ttyX-mode was not, we only need to
896 			 * check bootenv.rc for that setting.
897 			 */
898 			if ((!console_mode_set) && (console == CONS_TTY))
899 				serial_init();
900 			return;
901 		}
902 
903 		for (i = 0; devnames[i] != NULL; i++) {
904 			int n;
905 
906 			for (n = 0; console_devices[n].name != NULL; n++) {
907 				consolep = &console_devices[n];
908 				if (strcmp(devnames[i], consolep->name) == 0) {
909 					cons = consolep->value;
910 					if (cons == CONS_TTY)
911 						ttyn = n;
912 				}
913 			}
914 			if (cons != CONS_INVALID)
915 				break;
916 		}
917 
918 #if defined(__xpv)
919 		/*
920 		 * if the hypervisor is using the currently selected console
921 		 * device then default to using the hypervisor as the console
922 		 * device.
923 		 */
924 		if (cons == console_hypervisor_device) {
925 			cons = CONS_HYPERVISOR;
926 			console_hypervisor_redirect = B_TRUE;
927 		}
928 #endif /* __xpv */
929 
930 		if ((cons == CONS_INVALID) || (cons == console)) {
931 			/*
932 			 * we're sticking with whatever the current setting is
933 			 */
934 			return;
935 		}
936 
937 		console = cons;
938 		if (cons == CONS_TTY) {
939 			tty_num = ttyn;
940 			serial_init();
941 			return;
942 		}
943 	} else {
944 		/*
945 		 * USB serial and GRAPHICS console
946 		 * we just collect data into a buffer
947 		 */
948 		extern void *defcons_init(size_t);
949 		defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
950 	}
951 }
952 
953 #if defined(__xpv)
954 boolean_t
955 bcons_hypervisor_redirect(void)
956 {
957 	return (console_hypervisor_redirect);
958 }
959 
960 void
961 bcons_device_change(int new_console)
962 {
963 	if (new_console < CONS_MIN || new_console > CONS_MAX)
964 		return;
965 
966 	/*
967 	 * If we are asked to switch the console to the hypervisor, that
968 	 * really means to switch the console to whichever device the
969 	 * hypervisor is/was using.
970 	 */
971 	if (new_console == CONS_HYPERVISOR)
972 		new_console = console_hypervisor_device;
973 
974 	console = new_console;
975 
976 	if (new_console == CONS_TTY) {
977 		tty_num = console_hypervisor_tty_num;
978 		serial_init();
979 	}
980 }
981 #endif /* __xpv */
982 
983 static void
984 defcons_putchar(int c)
985 {
986 	if (defcons_buf != NULL &&
987 	    defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
988 		*defcons_cur++ = c;
989 		*defcons_cur = 0;
990 	}
991 }
992 #endif	/* _BOOT */
993 
994 static void
995 serial_putchar(int c)
996 {
997 	int checks = 10000;
998 
999 	while (((inb(port + LSR) & XHRE) == 0) && checks--)
1000 		;
1001 	outb(port + DAT, (char)c);
1002 }
1003 
1004 static int
1005 serial_getchar(void)
1006 {
1007 	uchar_t lsr;
1008 
1009 	while (serial_ischar() == 0)
1010 		;
1011 
1012 	lsr = inb(port + LSR);
1013 	if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
1014 	    SERIAL_PARITY | SERIAL_OVERRUN)) {
1015 		if (lsr & SERIAL_OVERRUN) {
1016 			return (inb(port + DAT));
1017 		} else {
1018 			/* Toss the garbage */
1019 			(void) inb(port + DAT);
1020 			return (0);
1021 		}
1022 	}
1023 	return (inb(port + DAT));
1024 }
1025 
1026 static int
1027 serial_ischar(void)
1028 {
1029 	return (inb(port + LSR) & RCA);
1030 }
1031 
1032 static void
1033 btem_control(btem_state_t *btem, int c)
1034 {
1035 	int y, rows, cols;
1036 
1037 	rows = fb_info.cursor.pos.y;
1038 	cols = fb_info.cursor.pos.x;
1039 
1040 	btem->btem_state = A_STATE_START;
1041 	switch (c) {
1042 	case A_BS:
1043 		bcons_dev.bd_setpos(rows, cols - 1);
1044 		break;
1045 
1046 	case A_HT:
1047 		cols += 8 - (cols % 8);
1048 		if (cols >= fb_info.terminal.x)
1049 			cols = fb_info.terminal.x - 1;
1050 		bcons_dev.bd_setpos(rows, cols);
1051 		break;
1052 
1053 	case A_CR:
1054 		bcons_dev.bd_setpos(rows, 0);
1055 		break;
1056 
1057 	case A_FF:
1058 		for (y = 0; y < fb_info.terminal.y; y++) {
1059 			bcons_dev.bd_setpos(y, 0);
1060 			bcons_dev.bd_eraseline();
1061 		}
1062 		bcons_dev.bd_setpos(0, 0);
1063 		break;
1064 
1065 	case A_ESC:
1066 		btem->btem_state = A_STATE_ESC;
1067 		break;
1068 
1069 	default:
1070 		bcons_dev.bd_putchar(c);
1071 		break;
1072 	}
1073 }
1074 
1075 /*
1076  * if parameters [0..count - 1] are not set, set them to the value
1077  * of newparam.
1078  */
1079 static void
1080 btem_setparam(btem_state_t *btem, int count, int newparam)
1081 {
1082 	int i;
1083 
1084 	for (i = 0; i < count; i++) {
1085 		if (btem->btem_params[i] == -1)
1086 			btem->btem_params[i] = newparam;
1087 	}
1088 }
1089 
1090 static void
1091 btem_chkparam(btem_state_t *btem, int c)
1092 {
1093 	int rows, cols;
1094 
1095 	rows = fb_info.cursor.pos.y;
1096 	cols = fb_info.cursor.pos.x;
1097 	switch (c) {
1098 	case '@':			/* insert char */
1099 		btem_setparam(btem, 1, 1);
1100 		bcons_dev.bd_shift(btem->btem_params[0]);
1101 		break;
1102 
1103 	case 'A':			/* cursor up */
1104 		btem_setparam(btem, 1, 1);
1105 		bcons_dev.bd_setpos(rows - btem->btem_params[0], cols);
1106 		break;
1107 
1108 	case 'B':			/* cursor down */
1109 		btem_setparam(btem, 1, 1);
1110 		bcons_dev.bd_setpos(rows + btem->btem_params[0], cols);
1111 		break;
1112 
1113 	case 'C':			/* cursor right */
1114 		btem_setparam(btem, 1, 1);
1115 		bcons_dev.bd_setpos(rows, cols + btem->btem_params[0]);
1116 		break;
1117 
1118 	case 'D':			/* cursor left */
1119 		btem_setparam(btem, 1, 1);
1120 		bcons_dev.bd_setpos(rows, cols - btem->btem_params[0]);
1121 		break;
1122 
1123 	case 'K':
1124 		bcons_dev.bd_eraseline();
1125 		break;
1126 	default:
1127 		/* bcons_dev.bd_putchar(c); */
1128 		break;
1129 	}
1130 	btem->btem_state = A_STATE_START;
1131 }
1132 
1133 static void
1134 btem_getparams(btem_state_t *btem, int c)
1135 {
1136 	if (isdigit(c)) {
1137 		btem->btem_paramval = btem->btem_paramval * 10 + c - '0';
1138 		btem->btem_gotparam = B_TRUE;
1139 		return;
1140 	}
1141 
1142 	if (btem->btem_curparam < BTEM_MAXPARAMS) {
1143 		if (btem->btem_gotparam == B_TRUE) {
1144 			btem->btem_params[btem->btem_curparam] =
1145 			    btem->btem_paramval;
1146 		}
1147 		btem->btem_curparam++;
1148 	}
1149 
1150 	if (c == ';') {
1151 		/* Restart parameter search */
1152 		btem->btem_gotparam = B_FALSE;
1153 		btem->btem_paramval = 0;
1154 	} else {
1155 		btem_chkparam(btem, c);
1156 	}
1157 }
1158 
1159 /* Simple boot terminal parser. */
1160 static void
1161 btem_parse(btem_state_t *btem, int c)
1162 {
1163 	int i;
1164 
1165 	/* Normal state? */
1166 	if (btem->btem_state == A_STATE_START) {
1167 		if (c == A_CSI || c < ' ')
1168 			btem_control(btem, c);
1169 		else
1170 			bcons_dev.bd_putchar(c);
1171 		return;
1172 	}
1173 
1174 	/* In <ESC> sequence */
1175 	if (btem->btem_state != A_STATE_ESC) {
1176 		btem_getparams(btem, c);
1177 		return;
1178 	}
1179 
1180 	/* Previous char was <ESC> */
1181 	switch (c) {
1182 	case '[':
1183 		btem->btem_curparam = 0;
1184 		btem->btem_paramval = 0;
1185 		btem->btem_gotparam = B_FALSE;
1186 		/* clear the parameters */
1187 		for (i = 0; i < BTEM_MAXPARAMS; i++)
1188 			btem->btem_params[i] = -1;
1189 		btem->btem_state = A_STATE_CSI;
1190 		return;
1191 
1192 	case 'Q':	/* <ESC>Q */
1193 	case 'C':	/* <ESC>C */
1194 		btem->btem_state = A_STATE_START;
1195 		return;
1196 
1197 	default:
1198 		btem->btem_state = A_STATE_START;
1199 		break;
1200 	}
1201 
1202 	if (c < ' ')
1203 		btem_control(btem, c);
1204 	else
1205 		bcons_dev.bd_putchar(c);
1206 }
1207 
1208 static void
1209 _doputchar(int device, int c)
1210 {
1211 	switch (device) {
1212 	case CONS_TTY:
1213 		serial_putchar(c);
1214 		return;
1215 	case CONS_SCREEN_TEXT:
1216 	case CONS_FRAMEBUFFER:
1217 		bcons_dev.bd_cursor(B_FALSE);
1218 		btem_parse(&boot_tem, c);
1219 		bcons_dev.bd_cursor(B_TRUE);
1220 		return;
1221 	case CONS_SCREEN_GRAPHICS:
1222 #if !defined(_BOOT)
1223 	case CONS_USBSER:
1224 		defcons_putchar(c);
1225 #endif /* _BOOT */
1226 	default:
1227 		return;
1228 	}
1229 }
1230 
1231 void
1232 bcons_putchar(int c)
1233 {
1234 #if defined(__xpv)
1235 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1236 	    console == CONS_HYPERVISOR) {
1237 		bcons_putchar_xen(c);
1238 		return;
1239 	}
1240 #endif /* __xpv */
1241 
1242 	if (c == '\n') {
1243 		_doputchar(console, '\r');
1244 		if (diag != console)
1245 			_doputchar(diag, '\r');
1246 	}
1247 	_doputchar(console, c);
1248 	if (diag != console)
1249 		_doputchar(diag, c);
1250 }
1251 
1252 /*
1253  * kernel character input functions
1254  */
1255 int
1256 bcons_getchar(void)
1257 {
1258 #if defined(__xpv)
1259 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1260 	    console == CONS_HYPERVISOR)
1261 		return (bcons_getchar_xen());
1262 #endif /* __xpv */
1263 
1264 	for (;;) {
1265 		if (console == CONS_TTY || diag == CONS_TTY) {
1266 			if (serial_ischar())
1267 				return (serial_getchar());
1268 		}
1269 		if (console != CONS_INVALID || diag != CONS_INVALID) {
1270 			if (kb_ischar())
1271 				return (kb_getchar());
1272 		}
1273 	}
1274 }
1275 
1276 #if !defined(_BOOT)
1277 
1278 int
1279 bcons_ischar(void)
1280 {
1281 	int c = 0;
1282 
1283 #if defined(__xpv)
1284 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1285 	    console == CONS_HYPERVISOR)
1286 		return (bcons_ischar_xen());
1287 #endif /* __xpv */
1288 
1289 	switch (console) {
1290 	case CONS_TTY:
1291 		c = serial_ischar();
1292 		break;
1293 
1294 	case CONS_INVALID:
1295 		break;
1296 
1297 	default:
1298 		c = kb_ischar();
1299 	}
1300 	if (c != 0)
1301 		return (c);
1302 
1303 	switch (diag) {
1304 	case CONS_TTY:
1305 		c = serial_ischar();
1306 		break;
1307 
1308 	case CONS_INVALID:
1309 		break;
1310 
1311 	default:
1312 		c = kb_ischar();
1313 	}
1314 
1315 	return (c);
1316 }
1317 
1318 #endif /* _BOOT */
1319