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