xref: /illumos-gate/usr/src/uts/i86pc/boot/boot_console.c (revision 632cbd96e544a0a21f1a1f7ca071b145f379215d)
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 - 7 */
633 	propval = find_boot_prop("tem.fg_color");
634 	if (propval != NULL) {
635 		intval = atoi(propval);
636 		if (intval >= 0 && intval <= 7)
637 			fb_info.fg_color = intval;
638 	}
639 
640 	/* color values are 0 - 7 */
641 	propval = find_boot_prop("tem.bg_color");
642 	if (propval != NULL && ISDIGIT(*propval)) {
643 		intval = atoi(propval);
644 		if (intval >= 0 && intval <= 7)
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) && defined(_NEWFONT)
662 	/*
663 	 * Load cursor position from bootloader only in dboot,
664 	 * dboot will pass cursor position to kernel via xboot info.
665 	 */
666 	/*
667 	 * To keep consistent console, we reset boot screen till new fonts
668 	 * are available.
669 	 */
670 	propval = find_boot_prop("tem.cursor.row");
671 	if (propval != NULL) {
672 		intval = atoi(propval);
673 		if (intval >= 0 && intval <= 0xFFFF)
674 			fb_info.cursor.pos.y = intval;
675 	}
676 
677 	propval = find_boot_prop("tem.cursor.col");
678 	if (propval != NULL) {
679 		intval = atoi(propval);
680 		if (intval >= 0 && intval <= 0xFFFF)
681 			fb_info.cursor.pos.x = intval;
682 	}
683 #endif
684 }
685 
686 /*
687  * Go through the console_devices array trying to match the string
688  * we were given.  The string on the command line must end with
689  * a comma or white space.
690  *
691  * This function does set tty_num as an side effect, this does imply that
692  * only one of the main console and the diag-device can be using serial.
693  */
694 static int
695 lookup_console_devices(const char *cons_str)
696 {
697 	int n, cons;
698 	size_t len, cons_len;
699 	console_value_t *consolep;
700 
701 	cons = CONS_INVALID;
702 	if (cons_str != NULL) {
703 
704 		cons_len = strlen(cons_str);
705 		for (n = 0; console_devices[n].name != NULL; n++) {
706 			consolep = &console_devices[n];
707 			len = strlen(consolep->name);
708 			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
709 			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
710 			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
711 			    (strncmp(cons_str, consolep->name, len) == 0)) {
712 				cons = consolep->value;
713 				if (cons == CONS_TTY)
714 					tty_num = n;
715 				break;
716 			}
717 		}
718 	}
719 	return (cons);
720 }
721 
722 void
723 bcons_init(struct xboot_info *xbi)
724 {
725 	const char *cons_str;
726 #if !defined(_BOOT)
727 	static char console_text[] = "text";
728 	extern int post_fastreboot;
729 #endif
730 
731 	if (xbi == NULL) {
732 		/* This is very early dboot console, set up ttya. */
733 		console = CONS_TTY;
734 		serial_init();
735 		return;
736 	}
737 
738 	/* Set up data to fetch properties from commad line and boot env. */
739 	boot_line = (char *)(uintptr_t)xbi->bi_cmdline;
740 	bcons_init_env(xbi);
741 	console = CONS_INVALID;
742 
743 	/* set up initial fb_info */
744 	bcons_init_fb();
745 
746 #if defined(__xpv)
747 	bcons_init_xen(boot_line);
748 #endif /* __xpv */
749 
750 	/*
751 	 * First check for diag-device.
752 	 */
753 	cons_str = find_boot_prop("diag-device");
754 	if (cons_str != NULL)
755 		diag = lookup_console_devices(cons_str);
756 
757 	cons_str = find_boot_prop("console");
758 	if (cons_str == NULL)
759 		cons_str = find_boot_prop("output-device");
760 
761 #if !defined(_BOOT)
762 	if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
763 		cons_str = console_text;
764 #endif
765 
766 	if (cons_str != NULL)
767 		console = lookup_console_devices(cons_str);
768 
769 #if defined(__xpv)
770 	/*
771 	 * domU's always use the hypervisor regardless of what
772 	 * the console variable may be set to.
773 	 */
774 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
775 		console = CONS_HYPERVISOR;
776 		console_hypervisor_redirect = B_TRUE;
777 	}
778 #endif /* __xpv */
779 
780 	/*
781 	 * If no console device specified, default to text.
782 	 * Remember what was specified for second phase.
783 	 */
784 	if (console == CONS_INVALID)
785 		console = CONS_SCREEN_TEXT;
786 #if !defined(_BOOT)
787 	else
788 		console_set = 1;
789 #endif
790 
791 #if defined(__xpv)
792 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
793 		switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
794 			case XEN_CONSOLE_COM1:
795 			case XEN_CONSOLE_COM2:
796 				console_hypervisor_device = CONS_TTY;
797 				console_hypervisor_tty_num = tty_num;
798 				break;
799 			case XEN_CONSOLE_VGA:
800 				/*
801 				 * Currently xen doesn't really support
802 				 * keyboard/display console devices.
803 				 * What this setting means is that
804 				 * "vga=keep" has been enabled, which is
805 				 * more of a xen debugging tool that a
806 				 * true console mode.  Hence, we're going
807 				 * to ignore this xen "console" setting.
808 				 */
809 				/*FALLTHROUGH*/
810 			default:
811 				console_hypervisor_device = CONS_INVALID;
812 		}
813 	}
814 
815 	/*
816 	 * if the hypervisor is using the currently selected serial
817 	 * port then default to using the hypervisor as the console
818 	 * device.
819 	 */
820 	if (console == console_hypervisor_device) {
821 		console = CONS_HYPERVISOR;
822 		console_hypervisor_redirect = B_TRUE;
823 	}
824 #endif /* __xpv */
825 
826 	/* make sure the FB is set up if present */
827 	console = boot_fb(xbi, console);
828 	switch (console) {
829 	case CONS_TTY:
830 		serial_init();
831 		break;
832 
833 	case CONS_HYPERVISOR:
834 		break;
835 
836 #if !defined(_BOOT)
837 	case CONS_USBSER:
838 		/*
839 		 * We can't do anything with the usb serial
840 		 * until we have memory management.
841 		 */
842 		break;
843 #endif
844 	case CONS_SCREEN_GRAPHICS:
845 		kb_init();
846 		break;
847 	case CONS_SCREEN_TEXT:
848 		boot_vga_init(&bcons_dev);
849 		/* Fall through */
850 	default:
851 		kb_init();
852 		break;
853 	}
854 
855 	/*
856 	 * Initialize diag device unless already done.
857 	 */
858 	switch (diag) {
859 	case CONS_TTY:
860 		if (console != CONS_TTY)
861 			serial_init();
862 		break;
863 	case CONS_SCREEN_GRAPHICS:
864 	case CONS_SCREEN_TEXT:
865 		if (console != CONS_SCREEN_GRAPHICS &&
866 		    console != CONS_SCREEN_TEXT)
867 			kb_init();
868 		break;
869 	default:
870 		break;
871 	}
872 }
873 
874 #if !defined(_BOOT)
875 /*
876  * 2nd part of console initialization.
877  * In the kernel (ie. fakebop), this can be used only to switch to
878  * using a serial port instead of screen based on the contents
879  * of the bootenv.rc file.
880  */
881 /*ARGSUSED*/
882 void
883 bcons_init2(char *inputdev, char *outputdev, char *consoledev)
884 {
885 	int cons = CONS_INVALID;
886 	int ttyn;
887 	char *devnames[] = { consoledev, outputdev, inputdev, NULL };
888 	console_value_t *consolep;
889 	int i;
890 	extern int post_fastreboot;
891 
892 	if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
893 		console = CONS_SCREEN_TEXT;
894 
895 	if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
896 		if (console_set) {
897 			/*
898 			 * If the console was set on the command line,
899 			 * but the ttyX-mode was not, we only need to
900 			 * check bootenv.rc for that setting.
901 			 */
902 			if ((!console_mode_set) && (console == CONS_TTY))
903 				serial_init();
904 			return;
905 		}
906 
907 		for (i = 0; devnames[i] != NULL; i++) {
908 			int n;
909 
910 			for (n = 0; console_devices[n].name != NULL; n++) {
911 				consolep = &console_devices[n];
912 				if (strcmp(devnames[i], consolep->name) == 0) {
913 					cons = consolep->value;
914 					if (cons == CONS_TTY)
915 						ttyn = n;
916 				}
917 			}
918 			if (cons != CONS_INVALID)
919 				break;
920 		}
921 
922 #if defined(__xpv)
923 		/*
924 		 * if the hypervisor is using the currently selected console
925 		 * device then default to using the hypervisor as the console
926 		 * device.
927 		 */
928 		if (cons == console_hypervisor_device) {
929 			cons = CONS_HYPERVISOR;
930 			console_hypervisor_redirect = B_TRUE;
931 		}
932 #endif /* __xpv */
933 
934 		if ((cons == CONS_INVALID) || (cons == console)) {
935 			/*
936 			 * we're sticking with whatever the current setting is
937 			 */
938 			return;
939 		}
940 
941 		console = cons;
942 		if (cons == CONS_TTY) {
943 			tty_num = ttyn;
944 			serial_init();
945 			return;
946 		}
947 	} else {
948 		/*
949 		 * USB serial and GRAPHICS console
950 		 * we just collect data into a buffer
951 		 */
952 		extern void *defcons_init(size_t);
953 		defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
954 	}
955 }
956 
957 #if defined(__xpv)
958 boolean_t
959 bcons_hypervisor_redirect(void)
960 {
961 	return (console_hypervisor_redirect);
962 }
963 
964 void
965 bcons_device_change(int new_console)
966 {
967 	if (new_console < CONS_MIN || new_console > CONS_MAX)
968 		return;
969 
970 	/*
971 	 * If we are asked to switch the console to the hypervisor, that
972 	 * really means to switch the console to whichever device the
973 	 * hypervisor is/was using.
974 	 */
975 	if (new_console == CONS_HYPERVISOR)
976 		new_console = console_hypervisor_device;
977 
978 	console = new_console;
979 
980 	if (new_console == CONS_TTY) {
981 		tty_num = console_hypervisor_tty_num;
982 		serial_init();
983 	}
984 }
985 #endif /* __xpv */
986 
987 static void
988 defcons_putchar(int c)
989 {
990 	if (defcons_buf != NULL &&
991 	    defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
992 		*defcons_cur++ = c;
993 		*defcons_cur = 0;
994 	}
995 }
996 #endif	/* _BOOT */
997 
998 static void
999 serial_putchar(int c)
1000 {
1001 	int checks = 10000;
1002 
1003 	while (((inb(port + LSR) & XHRE) == 0) && checks--)
1004 		;
1005 	outb(port + DAT, (char)c);
1006 }
1007 
1008 static int
1009 serial_getchar(void)
1010 {
1011 	uchar_t lsr;
1012 
1013 	while (serial_ischar() == 0)
1014 		;
1015 
1016 	lsr = inb(port + LSR);
1017 	if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
1018 	    SERIAL_PARITY | SERIAL_OVERRUN)) {
1019 		if (lsr & SERIAL_OVERRUN) {
1020 			return (inb(port + DAT));
1021 		} else {
1022 			/* Toss the garbage */
1023 			(void) inb(port + DAT);
1024 			return (0);
1025 		}
1026 	}
1027 	return (inb(port + DAT));
1028 }
1029 
1030 static int
1031 serial_ischar(void)
1032 {
1033 	return (inb(port + LSR) & RCA);
1034 }
1035 
1036 static void
1037 btem_control(btem_state_t *btem, int c)
1038 {
1039 	int y, rows, cols;
1040 
1041 	rows = fb_info.cursor.pos.y;
1042 	cols = fb_info.cursor.pos.x;
1043 
1044 	btem->btem_state = A_STATE_START;
1045 	switch (c) {
1046 	case A_BS:
1047 		bcons_dev.bd_setpos(rows, cols - 1);
1048 		break;
1049 
1050 	case A_HT:
1051 		cols += 8 - (cols % 8);
1052 		if (cols >= fb_info.terminal.x)
1053 			cols = fb_info.terminal.x - 1;
1054 		bcons_dev.bd_setpos(rows, cols);
1055 		break;
1056 
1057 	case A_CR:
1058 		bcons_dev.bd_setpos(rows, 0);
1059 		break;
1060 
1061 	case A_FF:
1062 		for (y = 0; y < fb_info.terminal.y; y++) {
1063 			bcons_dev.bd_setpos(y, 0);
1064 			bcons_dev.bd_eraseline();
1065 		}
1066 		bcons_dev.bd_setpos(0, 0);
1067 		break;
1068 
1069 	case A_ESC:
1070 		btem->btem_state = A_STATE_ESC;
1071 		break;
1072 
1073 	default:
1074 		bcons_dev.bd_putchar(c);
1075 		break;
1076 	}
1077 }
1078 
1079 /*
1080  * if parameters [0..count - 1] are not set, set them to the value
1081  * of newparam.
1082  */
1083 static void
1084 btem_setparam(btem_state_t *btem, int count, int newparam)
1085 {
1086 	int i;
1087 
1088 	for (i = 0; i < count; i++) {
1089 		if (btem->btem_params[i] == -1)
1090 			btem->btem_params[i] = newparam;
1091 	}
1092 }
1093 
1094 static void
1095 btem_chkparam(btem_state_t *btem, int c)
1096 {
1097 	int rows, cols;
1098 
1099 	rows = fb_info.cursor.pos.y;
1100 	cols = fb_info.cursor.pos.x;
1101 	switch (c) {
1102 	case '@':			/* insert char */
1103 		btem_setparam(btem, 1, 1);
1104 		bcons_dev.bd_shift(btem->btem_params[0]);
1105 		break;
1106 
1107 	case 'A':			/* cursor up */
1108 		btem_setparam(btem, 1, 1);
1109 		bcons_dev.bd_setpos(rows - btem->btem_params[0], cols);
1110 		break;
1111 
1112 	case 'B':			/* cursor down */
1113 		btem_setparam(btem, 1, 1);
1114 		bcons_dev.bd_setpos(rows + btem->btem_params[0], cols);
1115 		break;
1116 
1117 	case 'C':			/* cursor right */
1118 		btem_setparam(btem, 1, 1);
1119 		bcons_dev.bd_setpos(rows, cols + btem->btem_params[0]);
1120 		break;
1121 
1122 	case 'D':			/* cursor left */
1123 		btem_setparam(btem, 1, 1);
1124 		bcons_dev.bd_setpos(rows, cols - btem->btem_params[0]);
1125 		break;
1126 
1127 	case 'K':
1128 		bcons_dev.bd_eraseline();
1129 		break;
1130 	default:
1131 		/* bcons_dev.bd_putchar(c); */
1132 		break;
1133 	}
1134 	btem->btem_state = A_STATE_START;
1135 }
1136 
1137 static void
1138 btem_getparams(btem_state_t *btem, int c)
1139 {
1140 	if (isdigit(c)) {
1141 		btem->btem_paramval = btem->btem_paramval * 10 + c - '0';
1142 		btem->btem_gotparam = B_TRUE;
1143 		return;
1144 	}
1145 
1146 	if (btem->btem_curparam < BTEM_MAXPARAMS) {
1147 		if (btem->btem_gotparam == B_TRUE) {
1148 			btem->btem_params[btem->btem_curparam] =
1149 			    btem->btem_paramval;
1150 		}
1151 		btem->btem_curparam++;
1152 	}
1153 
1154 	if (c == ';') {
1155 		/* Restart parameter search */
1156 		btem->btem_gotparam = B_FALSE;
1157 		btem->btem_paramval = 0;
1158 	} else {
1159 		btem_chkparam(btem, c);
1160 	}
1161 }
1162 
1163 /* Simple boot terminal parser. */
1164 static void
1165 btem_parse(btem_state_t *btem, int c)
1166 {
1167 	int i;
1168 
1169 	/* Normal state? */
1170 	if (btem->btem_state == A_STATE_START) {
1171 		if (c == A_CSI || c < ' ')
1172 			btem_control(btem, c);
1173 		else
1174 			bcons_dev.bd_putchar(c);
1175 		return;
1176 	}
1177 
1178 	/* In <ESC> sequence */
1179 	if (btem->btem_state != A_STATE_ESC) {
1180 		btem_getparams(btem, c);
1181 		return;
1182 	}
1183 
1184 	/* Previous char was <ESC> */
1185 	switch (c) {
1186 	case '[':
1187 		btem->btem_curparam = 0;
1188 		btem->btem_paramval = 0;
1189 		btem->btem_gotparam = B_FALSE;
1190 		/* clear the parameters */
1191 		for (i = 0; i < BTEM_MAXPARAMS; i++)
1192 			btem->btem_params[i] = -1;
1193 		btem->btem_state = A_STATE_CSI;
1194 		return;
1195 
1196 	case 'Q':	/* <ESC>Q */
1197 	case 'C':	/* <ESC>C */
1198 		btem->btem_state = A_STATE_START;
1199 		return;
1200 
1201 	default:
1202 		btem->btem_state = A_STATE_START;
1203 		break;
1204 	}
1205 
1206 	if (c < ' ')
1207 		btem_control(btem, c);
1208 	else
1209 		bcons_dev.bd_putchar(c);
1210 }
1211 
1212 static void
1213 _doputchar(int device, int c)
1214 {
1215 	switch (device) {
1216 	case CONS_TTY:
1217 		serial_putchar(c);
1218 		return;
1219 	case CONS_SCREEN_TEXT:
1220 	case CONS_FRAMEBUFFER:
1221 		bcons_dev.bd_cursor(B_FALSE);
1222 		btem_parse(&boot_tem, c);
1223 		bcons_dev.bd_cursor(B_TRUE);
1224 		return;
1225 	case CONS_SCREEN_GRAPHICS:
1226 #if !defined(_BOOT)
1227 	case CONS_USBSER:
1228 		defcons_putchar(c);
1229 #endif /* _BOOT */
1230 	default:
1231 		return;
1232 	}
1233 }
1234 
1235 void
1236 bcons_putchar(int c)
1237 {
1238 #if defined(__xpv)
1239 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1240 	    console == CONS_HYPERVISOR) {
1241 		bcons_putchar_xen(c);
1242 		return;
1243 	}
1244 #endif /* __xpv */
1245 
1246 	if (c == '\n') {
1247 		_doputchar(console, '\r');
1248 		if (diag != console)
1249 			_doputchar(diag, '\r');
1250 	}
1251 	_doputchar(console, c);
1252 	if (diag != console)
1253 		_doputchar(diag, c);
1254 }
1255 
1256 /*
1257  * kernel character input functions
1258  */
1259 int
1260 bcons_getchar(void)
1261 {
1262 #if defined(__xpv)
1263 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1264 	    console == CONS_HYPERVISOR)
1265 		return (bcons_getchar_xen());
1266 #endif /* __xpv */
1267 
1268 	for (;;) {
1269 		if (console == CONS_TTY || diag == CONS_TTY) {
1270 			if (serial_ischar())
1271 				return (serial_getchar());
1272 		}
1273 		if (console != CONS_INVALID || diag != CONS_INVALID) {
1274 			if (kb_ischar())
1275 				return (kb_getchar());
1276 		}
1277 	}
1278 }
1279 
1280 #if !defined(_BOOT)
1281 
1282 int
1283 bcons_ischar(void)
1284 {
1285 	int c = 0;
1286 
1287 #if defined(__xpv)
1288 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1289 	    console == CONS_HYPERVISOR)
1290 		return (bcons_ischar_xen());
1291 #endif /* __xpv */
1292 
1293 	switch (console) {
1294 	case CONS_TTY:
1295 		c = serial_ischar();
1296 		break;
1297 
1298 	case CONS_INVALID:
1299 		break;
1300 
1301 	default:
1302 		c = kb_ischar();
1303 	}
1304 	if (c != 0)
1305 		return (c);
1306 
1307 	switch (diag) {
1308 	case CONS_TTY:
1309 		c = serial_ischar();
1310 		break;
1311 
1312 	case CONS_INVALID:
1313 		break;
1314 
1315 	default:
1316 		c = kb_ischar();
1317 	}
1318 
1319 	return (c);
1320 }
1321 
1322 #endif /* _BOOT */
1323