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