xref: /onnv-gate/usr/src/uts/i86pc/boot/boot_console.c (revision 10175:dd9708d1f561)
13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj /*
228960SJan.Setje-Eilers@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
233446Smrj  * Use is subject to license terms.
243446Smrj  */
253446Smrj 
263446Smrj #include <sys/types.h>
273446Smrj #include <sys/systm.h>
283446Smrj #include <sys/archsystm.h>
293446Smrj #include <sys/boot_console.h>
305084Sjohnlev #include <sys/panic.h>
314088Srscott #include <sys/ctype.h>
325084Sjohnlev #if defined(__xpv)
335084Sjohnlev #include <sys/hypervisor.h>
345084Sjohnlev #endif /* __xpv */
353446Smrj 
363446Smrj #include "boot_serial.h"
373446Smrj #include "boot_vga.h"
383446Smrj 
393446Smrj #if defined(_BOOT)
405084Sjohnlev #include <dboot/dboot_asm.h>
414088Srscott #include <dboot/dboot_xboot.h>
425084Sjohnlev #else /* _BOOT */
433446Smrj #include <sys/bootconf.h>
445084Sjohnlev #if defined(__xpv)
455084Sjohnlev #include <sys/evtchn_impl.h>
465084Sjohnlev #endif /* __xpv */
478960SJan.Setje-Eilers@Sun.COM static char *defcons_buf;
488960SJan.Setje-Eilers@Sun.COM static char *defcons_cur;
495084Sjohnlev #endif /* _BOOT */
505084Sjohnlev 
515084Sjohnlev #if defined(__xpv)
525084Sjohnlev extern void bcons_init_xen(char *);
535084Sjohnlev extern void bcons_putchar_xen(int);
545084Sjohnlev extern int bcons_getchar_xen(void);
555084Sjohnlev extern int bcons_ischar_xen(void);
565084Sjohnlev #endif /* __xpv */
573446Smrj 
583446Smrj static int cons_color = CONS_COLOR;
593446Smrj int console = CONS_SCREEN_TEXT;
605084Sjohnlev #if defined(__xpv)
615084Sjohnlev static int console_hypervisor_redirect = B_FALSE;
62*10175SStuart.Maybee@Sun.COM int console_hypervisor_device = CONS_INVALID;
635084Sjohnlev #endif /* __xpv */
645084Sjohnlev 
653446Smrj static int serial_ischar(void);
663446Smrj static int serial_getchar(void);
673446Smrj static void serial_putchar(int);
683446Smrj static void serial_adjust_prop(void);
693446Smrj 
703446Smrj static char *boot_line = NULL;
713446Smrj 
725084Sjohnlev #if !defined(_BOOT)
734088Srscott /* Set if the console or mode are expressed in the boot line */
744088Srscott static int console_set, console_mode_set;
755084Sjohnlev #endif
764088Srscott 
773446Smrj /* Clear the screen and initialize VIDEO, XPOS and YPOS. */
785084Sjohnlev void
793446Smrj clear_screen(void)
803446Smrj {
813446Smrj 	/*
823446Smrj 	 * XXX should set vga mode so we don't depend on the
835084Sjohnlev 	 * state left by the boot loader.  Note that we have to
845084Sjohnlev 	 * enable the cursor before clearing the screen since
855084Sjohnlev 	 * the cursor position is dependant upon the cursor
865084Sjohnlev 	 * skew, which is initialized by vga_cursor_display()
873446Smrj 	 */
885084Sjohnlev 	vga_cursor_display();
893446Smrj 	vga_clear(cons_color);
903446Smrj 	vga_setpos(0, 0);
913446Smrj }
923446Smrj 
933446Smrj /* Put the character C on the screen. */
943446Smrj static void
953446Smrj screen_putchar(int c)
963446Smrj {
973446Smrj 	int row, col;
983446Smrj 
993446Smrj 	vga_getpos(&row, &col);
1003446Smrj 	switch (c) {
1013446Smrj 	case '\t':
1023446Smrj 		col += 8 - (col % 8);
1033446Smrj 		if (col == VGA_TEXT_COLS)
1043446Smrj 			col = 79;
1053446Smrj 		vga_setpos(row, col);
1063446Smrj 		break;
1073446Smrj 
1083446Smrj 	case '\r':
1093446Smrj 		vga_setpos(row, 0);
1103446Smrj 		break;
1113446Smrj 
1123446Smrj 	case '\b':
1133446Smrj 		if (col > 0)
1143446Smrj 			vga_setpos(row, col - 1);
1153446Smrj 		break;
1163446Smrj 
1173446Smrj 	case '\n':
1183446Smrj 		if (row < VGA_TEXT_ROWS - 1)
1193446Smrj 			vga_setpos(row + 1, col);
1203446Smrj 		else
1213446Smrj 			vga_scroll(cons_color);
1223446Smrj 		break;
1233446Smrj 
1243446Smrj 	default:
1253446Smrj 		vga_drawc(c, cons_color);
1263446Smrj 		if (col < VGA_TEXT_COLS -1)
1273446Smrj 			vga_setpos(row, col + 1);
1283446Smrj 		else if (row < VGA_TEXT_ROWS - 1)
1293446Smrj 			vga_setpos(row + 1, 0);
1303446Smrj 		else {
1313446Smrj 			vga_setpos(row, 0);
1323446Smrj 			vga_scroll(cons_color);
1333446Smrj 		}
1343446Smrj 		break;
1353446Smrj 	}
1363446Smrj }
1373446Smrj 
1383446Smrj static int port;
1393446Smrj 
1403446Smrj static void
1413446Smrj serial_init(void)
1423446Smrj {
1433446Smrj 	switch (console) {
1443446Smrj 	case CONS_TTYA:
1453446Smrj 		port = 0x3f8;
1463446Smrj 		break;
1473446Smrj 	case CONS_TTYB:
1483446Smrj 		port = 0x2f8;
1493446Smrj 		break;
1503446Smrj 	}
1513446Smrj 
1523446Smrj 	outb(port + ISR, 0x20);
1533446Smrj 	if (inb(port + ISR) & 0x20) {
1543446Smrj 		/*
1553446Smrj 		 * 82510 chip is present
1563446Smrj 		 */
1573446Smrj 		outb(port + DAT+7, 0x04);	/* clear status */
1583446Smrj 		outb(port + ISR, 0x40);  /* set to bank 2 */
1593446Smrj 		outb(port + MCR, 0x08);  /* IMD */
1603446Smrj 		outb(port + DAT, 0x21);  /* FMD */
1613446Smrj 		outb(port + ISR, 0x00);  /* set to bank 0 */
1623446Smrj 	} else {
1633446Smrj 		/*
1643446Smrj 		 * set the UART in FIFO mode if it has FIFO buffers.
1653446Smrj 		 * use 16550 fifo reset sequence specified in NS
1663446Smrj 		 * application note. disable fifos until chip is
1673446Smrj 		 * initialized.
1683446Smrj 		 */
1693446Smrj 		outb(port + FIFOR, 0x00);		/* clear */
1703446Smrj 		outb(port + FIFOR, FIFO_ON);		/* enable */
1713446Smrj 		outb(port + FIFOR, FIFO_ON|FIFORXFLSH);  /* reset */
1723446Smrj 		outb(port + FIFOR,
1733446Smrj 		    FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
1743446Smrj 		if ((inb(port + ISR) & 0xc0) != 0xc0) {
1753446Smrj 			/*
1763446Smrj 			 * no fifo buffers so disable fifos.
1773446Smrj 			 * this is true for 8250's
1783446Smrj 			 */
1793446Smrj 			outb(port + FIFOR, 0x00);
1803446Smrj 		}
1813446Smrj 	}
1823446Smrj 
1833446Smrj 	/* disable interrupts */
1843446Smrj 	outb(port + ICR, 0);
1853446Smrj 
1865084Sjohnlev #if !defined(_BOOT)
1875084Sjohnlev 	if (IN_XPV_PANIC())
1885084Sjohnlev 		return;
1895084Sjohnlev #endif
1905084Sjohnlev 
1913446Smrj 	/* adjust setting based on tty properties */
1923446Smrj 	serial_adjust_prop();
1933446Smrj 
1943446Smrj #if defined(_BOOT)
1953446Smrj 	/*
1963446Smrj 	 * Do a full reset to match console behavior.
1973446Smrj 	 * 0x1B + c - reset everything
1983446Smrj 	 */
1993446Smrj 	serial_putchar(0x1B);
2003446Smrj 	serial_putchar('c');
2013446Smrj #endif
2023446Smrj }
2033446Smrj 
2044088Srscott /* Advance str pointer past white space */
2054088Srscott #define	EAT_WHITE_SPACE(str)	{			\
2064088Srscott 	while ((*str != '\0') && ISSPACE(*str))		\
2074088Srscott 		str++;					\
2084088Srscott }
2094088Srscott 
2104088Srscott /*
2114088Srscott  * boot_line is set when we call here.  Search it for the argument name,
2124088Srscott  * and if found, return a pointer to it.
2134088Srscott  */
2144088Srscott static char *
2154088Srscott find_boot_line_prop(const char *name)
2164088Srscott {
2174088Srscott 	char *ptr;
21810092SEnrico.Perla@Sun.COM 	char *ret = NULL;
2194088Srscott 	char end_char;
2204088Srscott 	size_t len;
2214088Srscott 
2224088Srscott 	if (boot_line == NULL)
2234088Srscott 		return (NULL);
2244088Srscott 
2254088Srscott 	len = strlen(name);
2264088Srscott 
2274088Srscott 	/*
2284088Srscott 	 * We have two nested loops here: the outer loop discards all options
2294088Srscott 	 * except -B, and the inner loop parses the -B options looking for
2304088Srscott 	 * the one we're interested in.
2314088Srscott 	 */
2324088Srscott 	for (ptr = boot_line; *ptr != '\0'; ptr++) {
2334088Srscott 		EAT_WHITE_SPACE(ptr);
2344088Srscott 
2354088Srscott 		if (*ptr == '-') {
2364088Srscott 			ptr++;
2374088Srscott 			while ((*ptr != '\0') && (*ptr != 'B') &&
2384088Srscott 			    !ISSPACE(*ptr))
2394088Srscott 				ptr++;
2404088Srscott 			if (*ptr == '\0')
24110092SEnrico.Perla@Sun.COM 				goto out;
2424088Srscott 			else if (*ptr != 'B')
2434088Srscott 				continue;
2444088Srscott 		} else {
2454088Srscott 			while ((*ptr != '\0') && !ISSPACE(*ptr))
2464088Srscott 				ptr++;
2474088Srscott 			if (*ptr == '\0')
24810092SEnrico.Perla@Sun.COM 				goto out;
2494088Srscott 			continue;
2504088Srscott 		}
2514088Srscott 
2524088Srscott 		do {
2534088Srscott 			ptr++;
2544088Srscott 			EAT_WHITE_SPACE(ptr);
2554088Srscott 
2564088Srscott 			if ((strncmp(ptr, name, len) == 0) &&
2574088Srscott 			    (ptr[len] == '=')) {
2584088Srscott 				ptr += len + 1;
25910092SEnrico.Perla@Sun.COM 				if ((*ptr == '\'') || (*ptr == '"')) {
26010092SEnrico.Perla@Sun.COM 					ret = ptr + 1;
26110092SEnrico.Perla@Sun.COM 					end_char = *ptr;
26210092SEnrico.Perla@Sun.COM 					ptr++;
26310092SEnrico.Perla@Sun.COM 				} else {
26410092SEnrico.Perla@Sun.COM 					ret = ptr;
26510092SEnrico.Perla@Sun.COM 					end_char = ',';
26610092SEnrico.Perla@Sun.COM 				}
26710092SEnrico.Perla@Sun.COM 				goto consume_property;
2684088Srscott 			}
2694088Srscott 
2704088Srscott 			/*
2714088Srscott 			 * We have a property, and it's not the one we're
2724088Srscott 			 * interested in.  Skip the property name.  A name
2734088Srscott 			 * can end with '=', a comma, or white space.
2744088Srscott 			 */
2754088Srscott 			while ((*ptr != '\0') && (*ptr != '=') &&
2764088Srscott 			    (*ptr != ',') && (!ISSPACE(*ptr)))
2774088Srscott 				ptr++;
2784088Srscott 
2794088Srscott 			/*
2804088Srscott 			 * We only want to go through the rest of the inner
2814088Srscott 			 * loop if we have a comma.  If we have a property
2824088Srscott 			 * name without a value, either continue or break.
2834088Srscott 			 */
2844088Srscott 			if (*ptr == '\0')
28510092SEnrico.Perla@Sun.COM 				goto out;
2864088Srscott 			else if (*ptr == ',')
2874088Srscott 				continue;
2884088Srscott 			else if (ISSPACE(*ptr))
2894088Srscott 				break;
2904088Srscott 			ptr++;
2914088Srscott 
2924088Srscott 			/*
2934088Srscott 			 * Is the property quoted?
2944088Srscott 			 */
2954088Srscott 			if ((*ptr == '\'') || (*ptr == '"')) {
2964088Srscott 				end_char = *ptr;
29710092SEnrico.Perla@Sun.COM 				ptr++;
2984088Srscott 			} else {
2994088Srscott 				/*
3004088Srscott 				 * Not quoted, so the string ends at a comma
3014088Srscott 				 * or at white space.  Deal with white space
3024088Srscott 				 * later.
3034088Srscott 				 */
3044088Srscott 				end_char = ',';
3054088Srscott 			}
3064088Srscott 
3074088Srscott 			/*
3084088Srscott 			 * Now, we can ignore any characters until we find
3094088Srscott 			 * end_char.
3104088Srscott 			 */
31110092SEnrico.Perla@Sun.COM consume_property:
3124088Srscott 			for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
3134088Srscott 				if ((end_char == ',') && ISSPACE(*ptr))
3144088Srscott 					break;
3154088Srscott 			}
31610092SEnrico.Perla@Sun.COM 			if (*ptr && (*ptr != ',') && !ISSPACE(*ptr))
3174088Srscott 				ptr++;
3184088Srscott 		} while (*ptr == ',');
3194088Srscott 	}
32010092SEnrico.Perla@Sun.COM out:
32110092SEnrico.Perla@Sun.COM 	return (ret);
3224088Srscott }
3234088Srscott 
3243446Smrj 
3253446Smrj #define	MATCHES(p, pat)	\
3263446Smrj 	(strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
3273446Smrj 
3283446Smrj #define	SKIP(p, c)				\
3293446Smrj 	while (*(p) != 0 && *p != (c))		\
3303446Smrj 		++(p);				\
3313446Smrj 	if (*(p) == (c))			\
3323446Smrj 		++(p);
3333446Smrj 
3343446Smrj /*
3353446Smrj  * find a tty mode property either from cmdline or from boot properties
3363446Smrj  */
3373446Smrj static char *
3383446Smrj get_mode_value(char *name)
3393446Smrj {
3403446Smrj 	/*
3413446Smrj 	 * when specified on boot line it looks like "name" "="....
3423446Smrj 	 */
3433446Smrj 	if (boot_line != NULL) {
3444088Srscott 		return (find_boot_line_prop(name));
3453446Smrj 	}
3463446Smrj 
3473446Smrj #if defined(_BOOT)
3483446Smrj 	return (NULL);
3493446Smrj #else
3503446Smrj 	/*
3513446Smrj 	 * if we're running in the full kernel we check the bootenv.rc settings
3523446Smrj 	 */
3533446Smrj 	{
3543446Smrj 		static char propval[20];
3553446Smrj 
3563446Smrj 		propval[0] = 0;
3574088Srscott 		if (do_bsys_getproplen(NULL, name) <= 0)
3583446Smrj 			return (NULL);
3594088Srscott 		(void) do_bsys_getprop(NULL, name, propval);
3603446Smrj 		return (propval);
3613446Smrj 	}
3623446Smrj #endif
3633446Smrj }
3643446Smrj 
3653446Smrj /*
3663446Smrj  * adjust serial port based on properties
3673446Smrj  * These come either from the cmdline or from boot properties.
3683446Smrj  */
3693446Smrj static void
3703446Smrj serial_adjust_prop(void)
3713446Smrj {
3723446Smrj 	char propname[20];
3733446Smrj 	char *propval;
3743446Smrj 	char *p;
3753446Smrj 	ulong_t baud;
3763446Smrj 	uchar_t lcr = 0;
3773446Smrj 	uchar_t mcr = DTR | RTS;
3783446Smrj 
3793446Smrj 	(void) strcpy(propname, "ttyX-mode");
3803446Smrj 	propname[3] = 'a' + console - CONS_TTYA;
3813446Smrj 	propval = get_mode_value(propname);
3823446Smrj 	if (propval == NULL)
3833446Smrj 		propval = "9600,8,n,1,-";
3845084Sjohnlev #if !defined(_BOOT)
3854088Srscott 	else
3864088Srscott 		console_mode_set = 1;
3875084Sjohnlev #endif
3883446Smrj 
3893446Smrj 	/* property is of the form: "9600,8,n,1,-" */
3903446Smrj 	p = propval;
3913446Smrj 	if (MATCHES(p, "110,"))
3923446Smrj 		baud = ASY110;
3933446Smrj 	else if (MATCHES(p, "150,"))
3943446Smrj 		baud = ASY150;
3953446Smrj 	else if (MATCHES(p, "300,"))
3963446Smrj 		baud = ASY300;
3973446Smrj 	else if (MATCHES(p, "600,"))
3983446Smrj 		baud = ASY600;
3993446Smrj 	else if (MATCHES(p, "1200,"))
4003446Smrj 		baud = ASY1200;
4013446Smrj 	else if (MATCHES(p, "2400,"))
4023446Smrj 		baud = ASY2400;
4033446Smrj 	else if (MATCHES(p, "4800,"))
4043446Smrj 		baud = ASY4800;
4053446Smrj 	else if (MATCHES(p, "19200,"))
4063446Smrj 		baud = ASY19200;
4073446Smrj 	else if (MATCHES(p, "38400,"))
4083446Smrj 		baud = ASY38400;
4093446Smrj 	else if (MATCHES(p, "57600,"))
4103446Smrj 		baud = ASY57600;
4113446Smrj 	else if (MATCHES(p, "115200,"))
4123446Smrj 		baud = ASY115200;
4133446Smrj 	else {
4143446Smrj 		baud = ASY9600;
4153446Smrj 		SKIP(p, ',');
4163446Smrj 	}
4173446Smrj 	outb(port + LCR, DLAB);
4183446Smrj 	outb(port + DAT + DLL, baud & 0xff);
4193446Smrj 	outb(port + DAT + DLH, (baud >> 8) & 0xff);
4203446Smrj 
4213446Smrj 	switch (*p) {
4223446Smrj 	case '5':
4233446Smrj 		lcr |= BITS5;
4243446Smrj 		++p;
4253446Smrj 		break;
4263446Smrj 	case '6':
4273446Smrj 		lcr |= BITS6;
4283446Smrj 		++p;
4293446Smrj 		break;
4303446Smrj 	case '7':
4313446Smrj 		lcr |= BITS7;
4323446Smrj 		++p;
4333446Smrj 		break;
4343446Smrj 	case '8':
4353446Smrj 		++p;
4363446Smrj 	default:
4373446Smrj 		lcr |= BITS8;
4383446Smrj 		break;
4393446Smrj 	}
4403446Smrj 
4413446Smrj 	SKIP(p, ',');
4423446Smrj 
4433446Smrj 	switch (*p) {
4443446Smrj 	case 'n':
4453446Smrj 		lcr |= PARITY_NONE;
4463446Smrj 		++p;
4473446Smrj 		break;
4483446Smrj 	case 'o':
4493446Smrj 		lcr |= PARITY_ODD;
4503446Smrj 		++p;
4513446Smrj 		break;
4523446Smrj 	case 'e':
4533446Smrj 		++p;
4543446Smrj 	default:
4553446Smrj 		lcr |= PARITY_EVEN;
4563446Smrj 		break;
4573446Smrj 	}
4583446Smrj 
4593446Smrj 
4603446Smrj 	SKIP(p, ',');
4613446Smrj 
4623446Smrj 	switch (*p) {
4633446Smrj 	case '1':
4643446Smrj 		/* STOP1 is 0 */
4653446Smrj 		++p;
4663446Smrj 		break;
4673446Smrj 	default:
4683446Smrj 		lcr |= STOP2;
4693446Smrj 		break;
4703446Smrj 	}
4713446Smrj 	/* set parity bits */
4723446Smrj 	outb(port + LCR, lcr);
4733446Smrj 
4743446Smrj 	(void) strcpy(propname, "ttyX-rts-dtr-off");
4753446Smrj 	propname[3] = 'a' + console - CONS_TTYA;
4763446Smrj 	propval = get_mode_value(propname);
4773446Smrj 	if (propval == NULL)
4783446Smrj 		propval = "false";
4793446Smrj 	if (propval[0] != 'f' && propval[0] != 'F')
4803446Smrj 		mcr = 0;
4813446Smrj 	/* set modem control bits */
4823446Smrj 	outb(port + MCR, mcr | OUT2);
4833446Smrj }
4843446Smrj 
4854088Srscott /*
4864088Srscott  * A structure to map console names to values.
4874088Srscott  */
4884088Srscott typedef struct {
4894088Srscott 	char *name;
4904088Srscott 	int value;
4914088Srscott } console_value_t;
4924088Srscott 
4934088Srscott console_value_t console_devices[] = {
4944088Srscott 	{ "ttya", CONS_TTYA },
4954088Srscott 	{ "ttyb", CONS_TTYB },
4964088Srscott 	{ "text", CONS_SCREEN_TEXT },
4977701SJan.Setje-Eilers@Sun.COM 	{ "graphics", CONS_SCREEN_GRAPHICS },
4985084Sjohnlev #if defined(__xpv)
4995084Sjohnlev 	{ "hypervisor", CONS_HYPERVISOR },
5005084Sjohnlev #endif
5014088Srscott #if !defined(_BOOT)
5024088Srscott 	{ "usb-serial", CONS_USBSER },
5034088Srscott #endif
5044088Srscott 	{ "", CONS_INVALID }
5054088Srscott };
5064088Srscott 
5073446Smrj void
5083446Smrj bcons_init(char *bootstr)
5093446Smrj {
5104088Srscott 	console_value_t *consolep;
5114088Srscott 	size_t len, cons_len;
5124088Srscott 	char *cons_str;
5134088Srscott 
5143446Smrj 	boot_line = bootstr;
5153446Smrj 	console = CONS_INVALID;
5163446Smrj 
5175084Sjohnlev #if defined(__xpv)
5185084Sjohnlev 	bcons_init_xen(bootstr);
5195084Sjohnlev #endif /* __xpv */
5205084Sjohnlev 
5214088Srscott 	cons_str = find_boot_line_prop("console");
5224088Srscott 	if (cons_str == NULL)
5234088Srscott 		cons_str = find_boot_line_prop("output-device");
5244088Srscott 
5254088Srscott 	/*
5264088Srscott 	 * Go through the console_devices array trying to match the string
5274088Srscott 	 * we were given.  The string on the command line must end with
5284088Srscott 	 * a comma or white space.
5294088Srscott 	 */
5304088Srscott 	if (cons_str != NULL) {
5314088Srscott 		cons_len = strlen(cons_str);
5324088Srscott 		consolep = console_devices;
5334088Srscott 		for (; consolep->name[0] != '\0'; consolep++) {
5344088Srscott 			len = strlen(consolep->name);
5354088Srscott 			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
5364088Srscott 			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
5374088Srscott 			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
5384088Srscott 			    (strncmp(cons_str, consolep->name, len) == 0)) {
5394088Srscott 				console = consolep->value;
5404088Srscott 				break;
5414088Srscott 			}
5424088Srscott 		}
5434088Srscott 	}
5443446Smrj 
5455084Sjohnlev #if defined(__xpv)
5465084Sjohnlev 	/*
5475084Sjohnlev 	 * domU's always use the hypervisor regardless of what
5485084Sjohnlev 	 * the console variable may be set to.
5495084Sjohnlev 	 */
5505084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
5515084Sjohnlev 		console = CONS_HYPERVISOR;
5525084Sjohnlev 		console_hypervisor_redirect = B_TRUE;
5535084Sjohnlev 	}
5545084Sjohnlev #endif /* __xpv */
5555084Sjohnlev 
5563446Smrj 	/*
5573446Smrj 	 * If no console device specified, default to text.
5583446Smrj 	 * Remember what was specified for second phase.
5593446Smrj 	 */
5603446Smrj 	if (console == CONS_INVALID)
5613446Smrj 		console = CONS_SCREEN_TEXT;
5625084Sjohnlev #if !defined(_BOOT)
5634088Srscott 	else
5644088Srscott 		console_set = 1;
5655084Sjohnlev #endif
5665084Sjohnlev 
5675084Sjohnlev #if defined(__xpv)
5685084Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
5695084Sjohnlev 		switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
5705084Sjohnlev 			case XEN_CONSOLE_COM1:
5715084Sjohnlev 				console_hypervisor_device = CONS_TTYA;
5725084Sjohnlev 				break;
5735084Sjohnlev 			case XEN_CONSOLE_COM2:
5745084Sjohnlev 				console_hypervisor_device = CONS_TTYB;
5755084Sjohnlev 				break;
5765084Sjohnlev 			case XEN_CONSOLE_VGA:
5775084Sjohnlev 				/*
5785084Sjohnlev 				 * Currently xen doesn't really support
5795084Sjohnlev 				 * keyboard/display console devices.
5805084Sjohnlev 				 * What this setting means is that
5815084Sjohnlev 				 * "vga=keep" has been enabled, which is
5825084Sjohnlev 				 * more of a xen debugging tool that a
5835084Sjohnlev 				 * true console mode.  Hence, we're going
5845084Sjohnlev 				 * to ignore this xen "console" setting.
5855084Sjohnlev 				 */
5865084Sjohnlev 				/*FALLTHROUGH*/
5875084Sjohnlev 			default:
5885084Sjohnlev 				console_hypervisor_device = CONS_INVALID;
5895084Sjohnlev 		}
5905084Sjohnlev 	}
5915084Sjohnlev 
5925084Sjohnlev 	/*
5935084Sjohnlev 	 * if the hypervisor is using the currently selected serial
5945084Sjohnlev 	 * port then default to using the hypervisor as the console
5955084Sjohnlev 	 * device.
5965084Sjohnlev 	 */
5975084Sjohnlev 	if (console == console_hypervisor_device) {
5985084Sjohnlev 		console = CONS_HYPERVISOR;
5995084Sjohnlev 		console_hypervisor_redirect = B_TRUE;
6005084Sjohnlev 	}
6015084Sjohnlev #endif /* __xpv */
6023446Smrj 
6033446Smrj 	switch (console) {
6043446Smrj 	case CONS_TTYA:
6053446Smrj 	case CONS_TTYB:
6063446Smrj 		serial_init();
6073446Smrj 		break;
6083446Smrj 
6095084Sjohnlev 	case CONS_HYPERVISOR:
6105084Sjohnlev 		break;
6115084Sjohnlev 
6124088Srscott #if !defined(_BOOT)
6134088Srscott 	case CONS_USBSER:
6144088Srscott 		/*
6154088Srscott 		 * We can't do anything with the usb serial
6164088Srscott 		 * until we have memory management.
6174088Srscott 		 */
6184088Srscott 		break;
6194088Srscott #endif
6207701SJan.Setje-Eilers@Sun.COM 	case CONS_SCREEN_GRAPHICS:
6217701SJan.Setje-Eilers@Sun.COM 		kb_init();
6227701SJan.Setje-Eilers@Sun.COM 		break;
6233446Smrj 	case CONS_SCREEN_TEXT:
6243446Smrj 	default:
6253446Smrj #if defined(_BOOT)
6265084Sjohnlev 		clear_screen();	/* clears the grub or xen screen */
6275084Sjohnlev #endif /* _BOOT */
6283446Smrj 		kb_init();
6293446Smrj 		break;
6303446Smrj 	}
6313446Smrj 	boot_line = NULL;
6323446Smrj }
6333446Smrj 
6345084Sjohnlev #if !defined(_BOOT)
6353446Smrj /*
6363446Smrj  * 2nd part of console initialization.
6373446Smrj  * In the kernel (ie. fakebop), this can be used only to switch to
6383446Smrj  * using a serial port instead of screen based on the contents
6393446Smrj  * of the bootenv.rc file.
6403446Smrj  */
6413446Smrj /*ARGSUSED*/
6423446Smrj void
6433446Smrj bcons_init2(char *inputdev, char *outputdev, char *consoledev)
6443446Smrj {
6453446Smrj 	int cons = CONS_INVALID;
6464088Srscott 	char *devnames[] = { consoledev, outputdev, inputdev, NULL };
6474088Srscott 	console_value_t *consolep;
6484088Srscott 	int i;
6493446Smrj 
65010092SEnrico.Perla@Sun.COM 	if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
6514088Srscott 		if (console_set) {
6524088Srscott 			/*
6534088Srscott 			 * If the console was set on the command line,
6544088Srscott 			 * but the ttyX-mode was not, we only need to
6554088Srscott 			 * check bootenv.rc for that setting.
6564088Srscott 			 */
6574088Srscott 			if ((!console_mode_set) &&
6584088Srscott 			    (console == CONS_TTYA || console == CONS_TTYB))
6594088Srscott 				serial_init();
6604088Srscott 			return;
6614088Srscott 		}
6623446Smrj 
6634088Srscott 		for (i = 0; devnames[i] != NULL; i++) {
6644088Srscott 			consolep = console_devices;
6654088Srscott 			for (; consolep->name[0] != '\0'; consolep++) {
6664088Srscott 				if (strcmp(devnames[i], consolep->name) == 0) {
6674088Srscott 					cons = consolep->value;
6684088Srscott 				}
6694088Srscott 			}
6704088Srscott 			if (cons != CONS_INVALID)
6714088Srscott 				break;
6724088Srscott 		}
6733446Smrj 
6745084Sjohnlev #if defined(__xpv)
6755084Sjohnlev 		/*
6765084Sjohnlev 		 * if the hypervisor is using the currently selected console
6775084Sjohnlev 		 * device then default to using the hypervisor as the console
6785084Sjohnlev 		 * device.
6795084Sjohnlev 		 */
6805084Sjohnlev 		if (cons == console_hypervisor_device) {
6815084Sjohnlev 			cons = CONS_HYPERVISOR;
6825084Sjohnlev 			console_hypervisor_redirect = B_TRUE;
6835084Sjohnlev 		}
6845084Sjohnlev #endif /* __xpv */
6855084Sjohnlev 
6865084Sjohnlev 		if ((cons == CONS_INVALID) || (cons == console)) {
6875084Sjohnlev 			/*
6885084Sjohnlev 			 * we're sticking with whatever the current setting is
6895084Sjohnlev 			 */
6904088Srscott 			return;
6915084Sjohnlev 		}
6923446Smrj 
6934088Srscott 		console = cons;
6944088Srscott 		if (cons == CONS_TTYA || cons == CONS_TTYB) {
6954088Srscott 			serial_init();
6964088Srscott 			return;
6974088Srscott 		}
69810092SEnrico.Perla@Sun.COM 	} else {
69910092SEnrico.Perla@Sun.COM 		/*
70010092SEnrico.Perla@Sun.COM 		 * USB serial and GRAPHICS console
70110092SEnrico.Perla@Sun.COM 		 * we just collect data into a buffer
70210092SEnrico.Perla@Sun.COM 		 */
7038960SJan.Setje-Eilers@Sun.COM 		extern void *defcons_init(size_t);
7048960SJan.Setje-Eilers@Sun.COM 		defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
7053446Smrj 	}
7065084Sjohnlev }
7075084Sjohnlev 
7085084Sjohnlev #if defined(__xpv)
7095084Sjohnlev boolean_t
7105084Sjohnlev bcons_hypervisor_redirect(void)
7115084Sjohnlev {
7125084Sjohnlev 	return (console_hypervisor_redirect);
7133446Smrj }
7143446Smrj 
7155084Sjohnlev void
7165084Sjohnlev bcons_device_change(int new_console)
7175084Sjohnlev {
7185084Sjohnlev 	if (new_console < CONS_MIN || new_console > CONS_MAX)
7195084Sjohnlev 		return;
7205084Sjohnlev 
7215084Sjohnlev 	/*
7225084Sjohnlev 	 * If we are asked to switch the console to the hypervisor, that
7235084Sjohnlev 	 * really means to switch the console to whichever device the
7245084Sjohnlev 	 * hypervisor is/was using.
7255084Sjohnlev 	 */
7265084Sjohnlev 	if (new_console == CONS_HYPERVISOR)
7275084Sjohnlev 		new_console = console_hypervisor_device;
7285084Sjohnlev 
7295084Sjohnlev 	console = new_console;
7305084Sjohnlev 
7315084Sjohnlev 	if (new_console == CONS_TTYA || new_console == CONS_TTYB)
7325084Sjohnlev 		serial_init();
7335084Sjohnlev }
7345084Sjohnlev #endif /* __xpv */
7355084Sjohnlev 
7363446Smrj static void
7378960SJan.Setje-Eilers@Sun.COM defcons_putchar(int c)
7383446Smrj {
73910092SEnrico.Perla@Sun.COM 	if (defcons_buf != NULL &&
74010092SEnrico.Perla@Sun.COM 	    defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
7418960SJan.Setje-Eilers@Sun.COM 		*defcons_cur++ = c;
7429462SJan.Setje-Eilers@Sun.COM 		*defcons_cur = 0;
7439462SJan.Setje-Eilers@Sun.COM 	}
7443446Smrj }
7453446Smrj #endif	/* _BOOT */
7463446Smrj 
7473446Smrj static void
7483446Smrj serial_putchar(int c)
7493446Smrj {
7503446Smrj 	int checks = 10000;
7513446Smrj 
7523446Smrj 	while (((inb(port + LSR) & XHRE) == 0) && checks--)
7533446Smrj 		;
7543446Smrj 	outb(port + DAT, (char)c);
7553446Smrj }
7563446Smrj 
7573446Smrj static int
7583446Smrj serial_getchar(void)
7593446Smrj {
7603446Smrj 	uchar_t lsr;
7613446Smrj 
7623446Smrj 	while (serial_ischar() == 0)
7633446Smrj 		;
7643446Smrj 
7653446Smrj 	lsr = inb(port + LSR);
7663446Smrj 	if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
7673446Smrj 	    SERIAL_PARITY | SERIAL_OVERRUN)) {
7683446Smrj 		if (lsr & SERIAL_OVERRUN) {
7693446Smrj 			return (inb(port + DAT));
7703446Smrj 		} else {
7713446Smrj 			/* Toss the garbage */
7723446Smrj 			(void) inb(port + DAT);
7733446Smrj 			return (0);
7743446Smrj 		}
7753446Smrj 	}
7763446Smrj 	return (inb(port + DAT));
7773446Smrj }
7783446Smrj 
7793446Smrj static int
7803446Smrj serial_ischar(void)
7813446Smrj {
7823446Smrj 	return (inb(port + LSR) & RCA);
7833446Smrj }
7843446Smrj 
7853446Smrj static void
7863446Smrj _doputchar(int c)
7873446Smrj {
7883446Smrj 	switch (console) {
7893446Smrj 	case CONS_TTYA:
7903446Smrj 	case CONS_TTYB:
7913446Smrj 		serial_putchar(c);
7923446Smrj 		return;
7933446Smrj 	case CONS_SCREEN_TEXT:
7943446Smrj 		screen_putchar(c);
7953446Smrj 		return;
7967701SJan.Setje-Eilers@Sun.COM 	case CONS_SCREEN_GRAPHICS:
7973446Smrj #if !defined(_BOOT)
7983446Smrj 	case CONS_USBSER:
7998960SJan.Setje-Eilers@Sun.COM 		defcons_putchar(c);
8008960SJan.Setje-Eilers@Sun.COM #endif /* _BOOT */
8013446Smrj 		return;
8023446Smrj 	}
8033446Smrj }
8043446Smrj 
8053446Smrj void
8063446Smrj bcons_putchar(int c)
8073446Smrj {
8083446Smrj 	static int bhcharpos = 0;
8093446Smrj 
8105084Sjohnlev #if defined(__xpv)
8115084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
8125084Sjohnlev 	    console == CONS_HYPERVISOR) {
8135084Sjohnlev 		bcons_putchar_xen(c);
8145084Sjohnlev 		return;
8155084Sjohnlev 	}
8165084Sjohnlev #endif /* __xpv */
8175084Sjohnlev 
8183446Smrj 	if (c == '\t') {
8193446Smrj 		do {
8203446Smrj 			_doputchar(' ');
8213446Smrj 		} while (++bhcharpos % 8);
8223446Smrj 		return;
8233446Smrj 	} else  if (c == '\n' || c == '\r') {
8243446Smrj 		bhcharpos = 0;
8253446Smrj 		_doputchar('\r');
8263446Smrj 		_doputchar(c);
8273446Smrj 		return;
8283446Smrj 	} else if (c == '\b') {
8293446Smrj 		if (bhcharpos)
8303446Smrj 			bhcharpos--;
8313446Smrj 		_doputchar(c);
8323446Smrj 		return;
8333446Smrj 	}
8343446Smrj 
8353446Smrj 	bhcharpos++;
8363446Smrj 	_doputchar(c);
8373446Smrj }
8383446Smrj 
8393446Smrj /*
8403446Smrj  * kernel character input functions
8413446Smrj  */
8423446Smrj int
8433446Smrj bcons_getchar(void)
8443446Smrj {
8455084Sjohnlev #if defined(__xpv)
8465084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
8475084Sjohnlev 	    console == CONS_HYPERVISOR)
8485084Sjohnlev 		return (bcons_getchar_xen());
8495084Sjohnlev #endif /* __xpv */
8505084Sjohnlev 
8513446Smrj 	switch (console) {
8523446Smrj 	case CONS_TTYA:
8533446Smrj 	case CONS_TTYB:
8543446Smrj 		return (serial_getchar());
8553446Smrj 	default:
8563446Smrj 		return (kb_getchar());
8573446Smrj 	}
8583446Smrj }
8593446Smrj 
8603446Smrj #if !defined(_BOOT)
8613446Smrj 
8623446Smrj int
8633446Smrj bcons_ischar(void)
8643446Smrj {
8655084Sjohnlev 
8665084Sjohnlev #if defined(__xpv)
8675084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
8685084Sjohnlev 	    console == CONS_HYPERVISOR)
8695084Sjohnlev 		return (bcons_ischar_xen());
8705084Sjohnlev #endif /* __xpv */
8715084Sjohnlev 
8723446Smrj 	switch (console) {
8733446Smrj 	case CONS_TTYA:
8743446Smrj 	case CONS_TTYB:
8753446Smrj 		return (serial_ischar());
8763446Smrj 	default:
8773446Smrj 		return (kb_ischar());
8783446Smrj 	}
8793446Smrj }
8803446Smrj 
8813446Smrj #endif /* _BOOT */
882