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