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