xref: /onnv-gate/usr/src/uts/i86pc/boot/boot_console.c (revision 10092:8e4d420530a8)
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;
625084Sjohnlev static 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 /* serial port stuff */
1395084Sjohnlev #if defined(__xpv) && defined(_BOOT)
1405084Sjohnlev static int
1415084Sjohnlev ec_probe_pirq(int pirq)
1425084Sjohnlev {
1435084Sjohnlev 	evtchn_bind_pirq_t bind;
1445084Sjohnlev 	evtchn_close_t close;
1455084Sjohnlev 
1465084Sjohnlev 	bind.pirq = pirq;
1475084Sjohnlev 	bind.flags = 0;
1485084Sjohnlev 	if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind) != 0)
1495084Sjohnlev 		return (0);
1505084Sjohnlev 
1515084Sjohnlev 	close.port = bind.port;
1525084Sjohnlev 	(void) HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
1535084Sjohnlev 	return (1);
1545084Sjohnlev }
1555084Sjohnlev #endif /* __xpv && _BOOT */
1565084Sjohnlev 
1573446Smrj static int port;
1583446Smrj 
1593446Smrj static void
1603446Smrj serial_init(void)
1613446Smrj {
1623446Smrj 	switch (console) {
1633446Smrj 	case CONS_TTYA:
1643446Smrj 		port = 0x3f8;
1653446Smrj 		break;
1663446Smrj 	case CONS_TTYB:
1673446Smrj 		port = 0x2f8;
1683446Smrj 		break;
1693446Smrj 	}
1703446Smrj 
1713446Smrj 	outb(port + ISR, 0x20);
1723446Smrj 	if (inb(port + ISR) & 0x20) {
1733446Smrj 		/*
1743446Smrj 		 * 82510 chip is present
1753446Smrj 		 */
1763446Smrj 		outb(port + DAT+7, 0x04);	/* clear status */
1773446Smrj 		outb(port + ISR, 0x40);  /* set to bank 2 */
1783446Smrj 		outb(port + MCR, 0x08);  /* IMD */
1793446Smrj 		outb(port + DAT, 0x21);  /* FMD */
1803446Smrj 		outb(port + ISR, 0x00);  /* set to bank 0 */
1813446Smrj 	} else {
1823446Smrj 		/*
1833446Smrj 		 * set the UART in FIFO mode if it has FIFO buffers.
1843446Smrj 		 * use 16550 fifo reset sequence specified in NS
1853446Smrj 		 * application note. disable fifos until chip is
1863446Smrj 		 * initialized.
1873446Smrj 		 */
1883446Smrj 		outb(port + FIFOR, 0x00);		/* clear */
1893446Smrj 		outb(port + FIFOR, FIFO_ON);		/* enable */
1903446Smrj 		outb(port + FIFOR, FIFO_ON|FIFORXFLSH);  /* reset */
1913446Smrj 		outb(port + FIFOR,
1923446Smrj 		    FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
1933446Smrj 		if ((inb(port + ISR) & 0xc0) != 0xc0) {
1943446Smrj 			/*
1953446Smrj 			 * no fifo buffers so disable fifos.
1963446Smrj 			 * this is true for 8250's
1973446Smrj 			 */
1983446Smrj 			outb(port + FIFOR, 0x00);
1993446Smrj 		}
2003446Smrj 	}
2013446Smrj 
2023446Smrj 	/* disable interrupts */
2033446Smrj 	outb(port + ICR, 0);
2043446Smrj 
2055084Sjohnlev #if !defined(_BOOT)
2065084Sjohnlev 	if (IN_XPV_PANIC())
2075084Sjohnlev 		return;
2085084Sjohnlev #endif
2095084Sjohnlev 
2103446Smrj 	/* adjust setting based on tty properties */
2113446Smrj 	serial_adjust_prop();
2123446Smrj 
2133446Smrj #if defined(_BOOT)
2143446Smrj 	/*
2153446Smrj 	 * Do a full reset to match console behavior.
2163446Smrj 	 * 0x1B + c - reset everything
2173446Smrj 	 */
2183446Smrj 	serial_putchar(0x1B);
2193446Smrj 	serial_putchar('c');
2203446Smrj #endif
2213446Smrj }
2223446Smrj 
2234088Srscott /* Advance str pointer past white space */
2244088Srscott #define	EAT_WHITE_SPACE(str)	{			\
2254088Srscott 	while ((*str != '\0') && ISSPACE(*str))		\
2264088Srscott 		str++;					\
2274088Srscott }
2284088Srscott 
2294088Srscott /*
2304088Srscott  * boot_line is set when we call here.  Search it for the argument name,
2314088Srscott  * and if found, return a pointer to it.
2324088Srscott  */
2334088Srscott static char *
2344088Srscott find_boot_line_prop(const char *name)
2354088Srscott {
2364088Srscott 	char *ptr;
237*10092SEnrico.Perla@Sun.COM 	char *ret = NULL;
2384088Srscott 	char end_char;
2394088Srscott 	size_t len;
2404088Srscott 
2414088Srscott 	if (boot_line == NULL)
2424088Srscott 		return (NULL);
2434088Srscott 
2444088Srscott 	len = strlen(name);
2454088Srscott 
2464088Srscott 	/*
2474088Srscott 	 * We have two nested loops here: the outer loop discards all options
2484088Srscott 	 * except -B, and the inner loop parses the -B options looking for
2494088Srscott 	 * the one we're interested in.
2504088Srscott 	 */
2514088Srscott 	for (ptr = boot_line; *ptr != '\0'; ptr++) {
2524088Srscott 		EAT_WHITE_SPACE(ptr);
2534088Srscott 
2544088Srscott 		if (*ptr == '-') {
2554088Srscott 			ptr++;
2564088Srscott 			while ((*ptr != '\0') && (*ptr != 'B') &&
2574088Srscott 			    !ISSPACE(*ptr))
2584088Srscott 				ptr++;
2594088Srscott 			if (*ptr == '\0')
260*10092SEnrico.Perla@Sun.COM 				goto out;
2614088Srscott 			else if (*ptr != 'B')
2624088Srscott 				continue;
2634088Srscott 		} else {
2644088Srscott 			while ((*ptr != '\0') && !ISSPACE(*ptr))
2654088Srscott 				ptr++;
2664088Srscott 			if (*ptr == '\0')
267*10092SEnrico.Perla@Sun.COM 				goto out;
2684088Srscott 			continue;
2694088Srscott 		}
2704088Srscott 
2714088Srscott 		do {
2724088Srscott 			ptr++;
2734088Srscott 			EAT_WHITE_SPACE(ptr);
2744088Srscott 
2754088Srscott 			if ((strncmp(ptr, name, len) == 0) &&
2764088Srscott 			    (ptr[len] == '=')) {
2774088Srscott 				ptr += len + 1;
278*10092SEnrico.Perla@Sun.COM 				if ((*ptr == '\'') || (*ptr == '"')) {
279*10092SEnrico.Perla@Sun.COM 					ret = ptr + 1;
280*10092SEnrico.Perla@Sun.COM 					end_char = *ptr;
281*10092SEnrico.Perla@Sun.COM 					ptr++;
282*10092SEnrico.Perla@Sun.COM 				} else {
283*10092SEnrico.Perla@Sun.COM 					ret = ptr;
284*10092SEnrico.Perla@Sun.COM 					end_char = ',';
285*10092SEnrico.Perla@Sun.COM 				}
286*10092SEnrico.Perla@Sun.COM 				goto consume_property;
2874088Srscott 			}
2884088Srscott 
2894088Srscott 			/*
2904088Srscott 			 * We have a property, and it's not the one we're
2914088Srscott 			 * interested in.  Skip the property name.  A name
2924088Srscott 			 * can end with '=', a comma, or white space.
2934088Srscott 			 */
2944088Srscott 			while ((*ptr != '\0') && (*ptr != '=') &&
2954088Srscott 			    (*ptr != ',') && (!ISSPACE(*ptr)))
2964088Srscott 				ptr++;
2974088Srscott 
2984088Srscott 			/*
2994088Srscott 			 * We only want to go through the rest of the inner
3004088Srscott 			 * loop if we have a comma.  If we have a property
3014088Srscott 			 * name without a value, either continue or break.
3024088Srscott 			 */
3034088Srscott 			if (*ptr == '\0')
304*10092SEnrico.Perla@Sun.COM 				goto out;
3054088Srscott 			else if (*ptr == ',')
3064088Srscott 				continue;
3074088Srscott 			else if (ISSPACE(*ptr))
3084088Srscott 				break;
3094088Srscott 			ptr++;
3104088Srscott 
3114088Srscott 			/*
3124088Srscott 			 * Is the property quoted?
3134088Srscott 			 */
3144088Srscott 			if ((*ptr == '\'') || (*ptr == '"')) {
3154088Srscott 				end_char = *ptr;
316*10092SEnrico.Perla@Sun.COM 				ptr++;
3174088Srscott 			} else {
3184088Srscott 				/*
3194088Srscott 				 * Not quoted, so the string ends at a comma
3204088Srscott 				 * or at white space.  Deal with white space
3214088Srscott 				 * later.
3224088Srscott 				 */
3234088Srscott 				end_char = ',';
3244088Srscott 			}
3254088Srscott 
3264088Srscott 			/*
3274088Srscott 			 * Now, we can ignore any characters until we find
3284088Srscott 			 * end_char.
3294088Srscott 			 */
330*10092SEnrico.Perla@Sun.COM consume_property:
3314088Srscott 			for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
3324088Srscott 				if ((end_char == ',') && ISSPACE(*ptr))
3334088Srscott 					break;
3344088Srscott 			}
335*10092SEnrico.Perla@Sun.COM 			if (*ptr && (*ptr != ',') && !ISSPACE(*ptr))
3364088Srscott 				ptr++;
3374088Srscott 		} while (*ptr == ',');
3384088Srscott 	}
339*10092SEnrico.Perla@Sun.COM out:
340*10092SEnrico.Perla@Sun.COM 	return (ret);
3414088Srscott }
3424088Srscott 
3433446Smrj 
3443446Smrj #define	MATCHES(p, pat)	\
3453446Smrj 	(strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
3463446Smrj 
3473446Smrj #define	SKIP(p, c)				\
3483446Smrj 	while (*(p) != 0 && *p != (c))		\
3493446Smrj 		++(p);				\
3503446Smrj 	if (*(p) == (c))			\
3513446Smrj 		++(p);
3523446Smrj 
3533446Smrj /*
3543446Smrj  * find a tty mode property either from cmdline or from boot properties
3553446Smrj  */
3563446Smrj static char *
3573446Smrj get_mode_value(char *name)
3583446Smrj {
3593446Smrj 	/*
3603446Smrj 	 * when specified on boot line it looks like "name" "="....
3613446Smrj 	 */
3623446Smrj 	if (boot_line != NULL) {
3634088Srscott 		return (find_boot_line_prop(name));
3643446Smrj 	}
3653446Smrj 
3663446Smrj #if defined(_BOOT)
3673446Smrj 	return (NULL);
3683446Smrj #else
3693446Smrj 	/*
3703446Smrj 	 * if we're running in the full kernel we check the bootenv.rc settings
3713446Smrj 	 */
3723446Smrj 	{
3733446Smrj 		static char propval[20];
3743446Smrj 
3753446Smrj 		propval[0] = 0;
3764088Srscott 		if (do_bsys_getproplen(NULL, name) <= 0)
3773446Smrj 			return (NULL);
3784088Srscott 		(void) do_bsys_getprop(NULL, name, propval);
3793446Smrj 		return (propval);
3803446Smrj 	}
3813446Smrj #endif
3823446Smrj }
3833446Smrj 
3843446Smrj /*
3853446Smrj  * adjust serial port based on properties
3863446Smrj  * These come either from the cmdline or from boot properties.
3873446Smrj  */
3883446Smrj static void
3893446Smrj serial_adjust_prop(void)
3903446Smrj {
3913446Smrj 	char propname[20];
3923446Smrj 	char *propval;
3933446Smrj 	char *p;
3943446Smrj 	ulong_t baud;
3953446Smrj 	uchar_t lcr = 0;
3963446Smrj 	uchar_t mcr = DTR | RTS;
3973446Smrj 
3983446Smrj 	(void) strcpy(propname, "ttyX-mode");
3993446Smrj 	propname[3] = 'a' + console - CONS_TTYA;
4003446Smrj 	propval = get_mode_value(propname);
4013446Smrj 	if (propval == NULL)
4023446Smrj 		propval = "9600,8,n,1,-";
4035084Sjohnlev #if !defined(_BOOT)
4044088Srscott 	else
4054088Srscott 		console_mode_set = 1;
4065084Sjohnlev #endif
4073446Smrj 
4083446Smrj 	/* property is of the form: "9600,8,n,1,-" */
4093446Smrj 	p = propval;
4103446Smrj 	if (MATCHES(p, "110,"))
4113446Smrj 		baud = ASY110;
4123446Smrj 	else if (MATCHES(p, "150,"))
4133446Smrj 		baud = ASY150;
4143446Smrj 	else if (MATCHES(p, "300,"))
4153446Smrj 		baud = ASY300;
4163446Smrj 	else if (MATCHES(p, "600,"))
4173446Smrj 		baud = ASY600;
4183446Smrj 	else if (MATCHES(p, "1200,"))
4193446Smrj 		baud = ASY1200;
4203446Smrj 	else if (MATCHES(p, "2400,"))
4213446Smrj 		baud = ASY2400;
4223446Smrj 	else if (MATCHES(p, "4800,"))
4233446Smrj 		baud = ASY4800;
4243446Smrj 	else if (MATCHES(p, "19200,"))
4253446Smrj 		baud = ASY19200;
4263446Smrj 	else if (MATCHES(p, "38400,"))
4273446Smrj 		baud = ASY38400;
4283446Smrj 	else if (MATCHES(p, "57600,"))
4293446Smrj 		baud = ASY57600;
4303446Smrj 	else if (MATCHES(p, "115200,"))
4313446Smrj 		baud = ASY115200;
4323446Smrj 	else {
4333446Smrj 		baud = ASY9600;
4343446Smrj 		SKIP(p, ',');
4353446Smrj 	}
4363446Smrj 	outb(port + LCR, DLAB);
4373446Smrj 	outb(port + DAT + DLL, baud & 0xff);
4383446Smrj 	outb(port + DAT + DLH, (baud >> 8) & 0xff);
4393446Smrj 
4403446Smrj 	switch (*p) {
4413446Smrj 	case '5':
4423446Smrj 		lcr |= BITS5;
4433446Smrj 		++p;
4443446Smrj 		break;
4453446Smrj 	case '6':
4463446Smrj 		lcr |= BITS6;
4473446Smrj 		++p;
4483446Smrj 		break;
4493446Smrj 	case '7':
4503446Smrj 		lcr |= BITS7;
4513446Smrj 		++p;
4523446Smrj 		break;
4533446Smrj 	case '8':
4543446Smrj 		++p;
4553446Smrj 	default:
4563446Smrj 		lcr |= BITS8;
4573446Smrj 		break;
4583446Smrj 	}
4593446Smrj 
4603446Smrj 	SKIP(p, ',');
4613446Smrj 
4623446Smrj 	switch (*p) {
4633446Smrj 	case 'n':
4643446Smrj 		lcr |= PARITY_NONE;
4653446Smrj 		++p;
4663446Smrj 		break;
4673446Smrj 	case 'o':
4683446Smrj 		lcr |= PARITY_ODD;
4693446Smrj 		++p;
4703446Smrj 		break;
4713446Smrj 	case 'e':
4723446Smrj 		++p;
4733446Smrj 	default:
4743446Smrj 		lcr |= PARITY_EVEN;
4753446Smrj 		break;
4763446Smrj 	}
4773446Smrj 
4783446Smrj 
4793446Smrj 	SKIP(p, ',');
4803446Smrj 
4813446Smrj 	switch (*p) {
4823446Smrj 	case '1':
4833446Smrj 		/* STOP1 is 0 */
4843446Smrj 		++p;
4853446Smrj 		break;
4863446Smrj 	default:
4873446Smrj 		lcr |= STOP2;
4883446Smrj 		break;
4893446Smrj 	}
4903446Smrj 	/* set parity bits */
4913446Smrj 	outb(port + LCR, lcr);
4923446Smrj 
4933446Smrj 	(void) strcpy(propname, "ttyX-rts-dtr-off");
4943446Smrj 	propname[3] = 'a' + console - CONS_TTYA;
4953446Smrj 	propval = get_mode_value(propname);
4963446Smrj 	if (propval == NULL)
4973446Smrj 		propval = "false";
4983446Smrj 	if (propval[0] != 'f' && propval[0] != 'F')
4993446Smrj 		mcr = 0;
5003446Smrj 	/* set modem control bits */
5013446Smrj 	outb(port + MCR, mcr | OUT2);
5023446Smrj }
5033446Smrj 
5044088Srscott /*
5054088Srscott  * A structure to map console names to values.
5064088Srscott  */
5074088Srscott typedef struct {
5084088Srscott 	char *name;
5094088Srscott 	int value;
5104088Srscott } console_value_t;
5114088Srscott 
5124088Srscott console_value_t console_devices[] = {
5134088Srscott 	{ "ttya", CONS_TTYA },
5144088Srscott 	{ "ttyb", CONS_TTYB },
5154088Srscott 	{ "text", CONS_SCREEN_TEXT },
5167701SJan.Setje-Eilers@Sun.COM 	{ "graphics", CONS_SCREEN_GRAPHICS },
5175084Sjohnlev #if defined(__xpv)
5185084Sjohnlev 	{ "hypervisor", CONS_HYPERVISOR },
5195084Sjohnlev #endif
5204088Srscott #if !defined(_BOOT)
5214088Srscott 	{ "usb-serial", CONS_USBSER },
5224088Srscott #endif
5234088Srscott 	{ "", CONS_INVALID }
5244088Srscott };
5254088Srscott 
5263446Smrj void
5273446Smrj bcons_init(char *bootstr)
5283446Smrj {
5294088Srscott 	console_value_t *consolep;
5304088Srscott 	size_t len, cons_len;
5314088Srscott 	char *cons_str;
5324088Srscott 
5333446Smrj 	boot_line = bootstr;
5343446Smrj 	console = CONS_INVALID;
5353446Smrj 
5365084Sjohnlev #if defined(__xpv)
5375084Sjohnlev 	bcons_init_xen(bootstr);
5385084Sjohnlev #endif /* __xpv */
5395084Sjohnlev 
5404088Srscott 	cons_str = find_boot_line_prop("console");
5414088Srscott 	if (cons_str == NULL)
5424088Srscott 		cons_str = find_boot_line_prop("output-device");
5434088Srscott 
5444088Srscott 	/*
5454088Srscott 	 * Go through the console_devices array trying to match the string
5464088Srscott 	 * we were given.  The string on the command line must end with
5474088Srscott 	 * a comma or white space.
5484088Srscott 	 */
5494088Srscott 	if (cons_str != NULL) {
5504088Srscott 		cons_len = strlen(cons_str);
5514088Srscott 		consolep = console_devices;
5524088Srscott 		for (; consolep->name[0] != '\0'; consolep++) {
5534088Srscott 			len = strlen(consolep->name);
5544088Srscott 			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
5554088Srscott 			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
5564088Srscott 			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
5574088Srscott 			    (strncmp(cons_str, consolep->name, len) == 0)) {
5584088Srscott 				console = consolep->value;
5594088Srscott 				break;
5604088Srscott 			}
5614088Srscott 		}
5624088Srscott 	}
5633446Smrj 
5645084Sjohnlev #if defined(__xpv)
5655084Sjohnlev 	/*
5665084Sjohnlev 	 * domU's always use the hypervisor regardless of what
5675084Sjohnlev 	 * the console variable may be set to.
5685084Sjohnlev 	 */
5695084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
5705084Sjohnlev 		console = CONS_HYPERVISOR;
5715084Sjohnlev 		console_hypervisor_redirect = B_TRUE;
5725084Sjohnlev 	}
5735084Sjohnlev #endif /* __xpv */
5745084Sjohnlev 
5753446Smrj 	/*
5763446Smrj 	 * If no console device specified, default to text.
5773446Smrj 	 * Remember what was specified for second phase.
5783446Smrj 	 */
5793446Smrj 	if (console == CONS_INVALID)
5803446Smrj 		console = CONS_SCREEN_TEXT;
5815084Sjohnlev #if !defined(_BOOT)
5824088Srscott 	else
5834088Srscott 		console_set = 1;
5845084Sjohnlev #endif
5855084Sjohnlev 
5865084Sjohnlev #if defined(__xpv)
5875084Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
5885084Sjohnlev 		switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
5895084Sjohnlev 			case XEN_CONSOLE_COM1:
5905084Sjohnlev 				console_hypervisor_device = CONS_TTYA;
5915084Sjohnlev 				break;
5925084Sjohnlev 			case XEN_CONSOLE_COM2:
5935084Sjohnlev 				console_hypervisor_device = CONS_TTYB;
5945084Sjohnlev 				break;
5955084Sjohnlev 			case XEN_CONSOLE_VGA:
5965084Sjohnlev 				/*
5975084Sjohnlev 				 * Currently xen doesn't really support
5985084Sjohnlev 				 * keyboard/display console devices.
5995084Sjohnlev 				 * What this setting means is that
6005084Sjohnlev 				 * "vga=keep" has been enabled, which is
6015084Sjohnlev 				 * more of a xen debugging tool that a
6025084Sjohnlev 				 * true console mode.  Hence, we're going
6035084Sjohnlev 				 * to ignore this xen "console" setting.
6045084Sjohnlev 				 */
6055084Sjohnlev 				/*FALLTHROUGH*/
6065084Sjohnlev 			default:
6075084Sjohnlev 				console_hypervisor_device = CONS_INVALID;
6085084Sjohnlev 		}
6095084Sjohnlev 	}
6105084Sjohnlev 
6115084Sjohnlev 	/*
6125084Sjohnlev 	 * if the hypervisor is using the currently selected serial
6135084Sjohnlev 	 * port then default to using the hypervisor as the console
6145084Sjohnlev 	 * device.
6155084Sjohnlev 	 */
6165084Sjohnlev 	if (console == console_hypervisor_device) {
6175084Sjohnlev 		console = CONS_HYPERVISOR;
6185084Sjohnlev 		console_hypervisor_redirect = B_TRUE;
6195084Sjohnlev 	}
6205084Sjohnlev #endif /* __xpv */
6213446Smrj 
6223446Smrj 	switch (console) {
6233446Smrj 	case CONS_TTYA:
6243446Smrj 	case CONS_TTYB:
6253446Smrj 		serial_init();
6263446Smrj 		break;
6273446Smrj 
6285084Sjohnlev 	case CONS_HYPERVISOR:
6295084Sjohnlev 		break;
6305084Sjohnlev 
6314088Srscott #if !defined(_BOOT)
6324088Srscott 	case CONS_USBSER:
6334088Srscott 		/*
6344088Srscott 		 * We can't do anything with the usb serial
6354088Srscott 		 * until we have memory management.
6364088Srscott 		 */
6374088Srscott 		break;
6384088Srscott #endif
6397701SJan.Setje-Eilers@Sun.COM 	case CONS_SCREEN_GRAPHICS:
6407701SJan.Setje-Eilers@Sun.COM 		kb_init();
6417701SJan.Setje-Eilers@Sun.COM 		break;
6423446Smrj 	case CONS_SCREEN_TEXT:
6433446Smrj 	default:
6443446Smrj #if defined(_BOOT)
6455084Sjohnlev 		clear_screen();	/* clears the grub or xen screen */
6465084Sjohnlev #endif /* _BOOT */
6473446Smrj 		kb_init();
6483446Smrj 		break;
6493446Smrj 	}
6503446Smrj 	boot_line = NULL;
6513446Smrj }
6523446Smrj 
6535084Sjohnlev #if !defined(_BOOT)
6543446Smrj /*
6553446Smrj  * 2nd part of console initialization.
6563446Smrj  * In the kernel (ie. fakebop), this can be used only to switch to
6573446Smrj  * using a serial port instead of screen based on the contents
6583446Smrj  * of the bootenv.rc file.
6593446Smrj  */
6603446Smrj /*ARGSUSED*/
6613446Smrj void
6623446Smrj bcons_init2(char *inputdev, char *outputdev, char *consoledev)
6633446Smrj {
6643446Smrj 	int cons = CONS_INVALID;
6654088Srscott 	char *devnames[] = { consoledev, outputdev, inputdev, NULL };
6664088Srscott 	console_value_t *consolep;
6674088Srscott 	int i;
6683446Smrj 
669*10092SEnrico.Perla@Sun.COM 	if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
6704088Srscott 		if (console_set) {
6714088Srscott 			/*
6724088Srscott 			 * If the console was set on the command line,
6734088Srscott 			 * but the ttyX-mode was not, we only need to
6744088Srscott 			 * check bootenv.rc for that setting.
6754088Srscott 			 */
6764088Srscott 			if ((!console_mode_set) &&
6774088Srscott 			    (console == CONS_TTYA || console == CONS_TTYB))
6784088Srscott 				serial_init();
6794088Srscott 			return;
6804088Srscott 		}
6813446Smrj 
6824088Srscott 		for (i = 0; devnames[i] != NULL; i++) {
6834088Srscott 			consolep = console_devices;
6844088Srscott 			for (; consolep->name[0] != '\0'; consolep++) {
6854088Srscott 				if (strcmp(devnames[i], consolep->name) == 0) {
6864088Srscott 					cons = consolep->value;
6874088Srscott 				}
6884088Srscott 			}
6894088Srscott 			if (cons != CONS_INVALID)
6904088Srscott 				break;
6914088Srscott 		}
6923446Smrj 
6935084Sjohnlev #if defined(__xpv)
6945084Sjohnlev 		/*
6955084Sjohnlev 		 * if the hypervisor is using the currently selected console
6965084Sjohnlev 		 * device then default to using the hypervisor as the console
6975084Sjohnlev 		 * device.
6985084Sjohnlev 		 */
6995084Sjohnlev 		if (cons == console_hypervisor_device) {
7005084Sjohnlev 			cons = CONS_HYPERVISOR;
7015084Sjohnlev 			console_hypervisor_redirect = B_TRUE;
7025084Sjohnlev 		}
7035084Sjohnlev #endif /* __xpv */
7045084Sjohnlev 
7055084Sjohnlev 		if ((cons == CONS_INVALID) || (cons == console)) {
7065084Sjohnlev 			/*
7075084Sjohnlev 			 * we're sticking with whatever the current setting is
7085084Sjohnlev 			 */
7094088Srscott 			return;
7105084Sjohnlev 		}
7113446Smrj 
7124088Srscott 		console = cons;
7134088Srscott 		if (cons == CONS_TTYA || cons == CONS_TTYB) {
7144088Srscott 			serial_init();
7154088Srscott 			return;
7164088Srscott 		}
717*10092SEnrico.Perla@Sun.COM 	} else {
718*10092SEnrico.Perla@Sun.COM 		/*
719*10092SEnrico.Perla@Sun.COM 		 * USB serial and GRAPHICS console
720*10092SEnrico.Perla@Sun.COM 		 * we just collect data into a buffer
721*10092SEnrico.Perla@Sun.COM 		 */
7228960SJan.Setje-Eilers@Sun.COM 		extern void *defcons_init(size_t);
7238960SJan.Setje-Eilers@Sun.COM 		defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
7243446Smrj 	}
7255084Sjohnlev }
7265084Sjohnlev 
7275084Sjohnlev #if defined(__xpv)
7285084Sjohnlev boolean_t
7295084Sjohnlev bcons_hypervisor_redirect(void)
7305084Sjohnlev {
7315084Sjohnlev 	return (console_hypervisor_redirect);
7323446Smrj }
7333446Smrj 
7345084Sjohnlev void
7355084Sjohnlev bcons_device_change(int new_console)
7365084Sjohnlev {
7375084Sjohnlev 	if (new_console < CONS_MIN || new_console > CONS_MAX)
7385084Sjohnlev 		return;
7395084Sjohnlev 
7405084Sjohnlev 	/*
7415084Sjohnlev 	 * If we are asked to switch the console to the hypervisor, that
7425084Sjohnlev 	 * really means to switch the console to whichever device the
7435084Sjohnlev 	 * hypervisor is/was using.
7445084Sjohnlev 	 */
7455084Sjohnlev 	if (new_console == CONS_HYPERVISOR)
7465084Sjohnlev 		new_console = console_hypervisor_device;
7475084Sjohnlev 
7485084Sjohnlev 	console = new_console;
7495084Sjohnlev 
7505084Sjohnlev 	if (new_console == CONS_TTYA || new_console == CONS_TTYB)
7515084Sjohnlev 		serial_init();
7525084Sjohnlev }
7535084Sjohnlev #endif /* __xpv */
7545084Sjohnlev 
7553446Smrj static void
7568960SJan.Setje-Eilers@Sun.COM defcons_putchar(int c)
7573446Smrj {
758*10092SEnrico.Perla@Sun.COM 	if (defcons_buf != NULL &&
759*10092SEnrico.Perla@Sun.COM 	    defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
7608960SJan.Setje-Eilers@Sun.COM 		*defcons_cur++ = c;
7619462SJan.Setje-Eilers@Sun.COM 		*defcons_cur = 0;
7629462SJan.Setje-Eilers@Sun.COM 	}
7633446Smrj }
7643446Smrj #endif	/* _BOOT */
7653446Smrj 
7663446Smrj static void
7673446Smrj serial_putchar(int c)
7683446Smrj {
7693446Smrj 	int checks = 10000;
7703446Smrj 
7713446Smrj 	while (((inb(port + LSR) & XHRE) == 0) && checks--)
7723446Smrj 		;
7733446Smrj 	outb(port + DAT, (char)c);
7743446Smrj }
7753446Smrj 
7763446Smrj static int
7773446Smrj serial_getchar(void)
7783446Smrj {
7793446Smrj 	uchar_t lsr;
7803446Smrj 
7813446Smrj 	while (serial_ischar() == 0)
7823446Smrj 		;
7833446Smrj 
7843446Smrj 	lsr = inb(port + LSR);
7853446Smrj 	if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
7863446Smrj 	    SERIAL_PARITY | SERIAL_OVERRUN)) {
7873446Smrj 		if (lsr & SERIAL_OVERRUN) {
7883446Smrj 			return (inb(port + DAT));
7893446Smrj 		} else {
7903446Smrj 			/* Toss the garbage */
7913446Smrj 			(void) inb(port + DAT);
7923446Smrj 			return (0);
7933446Smrj 		}
7943446Smrj 	}
7953446Smrj 	return (inb(port + DAT));
7963446Smrj }
7973446Smrj 
7983446Smrj static int
7993446Smrj serial_ischar(void)
8003446Smrj {
8013446Smrj 	return (inb(port + LSR) & RCA);
8023446Smrj }
8033446Smrj 
8043446Smrj static void
8053446Smrj _doputchar(int c)
8063446Smrj {
8073446Smrj 	switch (console) {
8083446Smrj 	case CONS_TTYA:
8093446Smrj 	case CONS_TTYB:
8103446Smrj 		serial_putchar(c);
8113446Smrj 		return;
8123446Smrj 	case CONS_SCREEN_TEXT:
8133446Smrj 		screen_putchar(c);
8143446Smrj 		return;
8157701SJan.Setje-Eilers@Sun.COM 	case CONS_SCREEN_GRAPHICS:
8163446Smrj #if !defined(_BOOT)
8173446Smrj 	case CONS_USBSER:
8188960SJan.Setje-Eilers@Sun.COM 		defcons_putchar(c);
8198960SJan.Setje-Eilers@Sun.COM #endif /* _BOOT */
8203446Smrj 		return;
8213446Smrj 	}
8223446Smrj }
8233446Smrj 
8243446Smrj void
8253446Smrj bcons_putchar(int c)
8263446Smrj {
8273446Smrj 	static int bhcharpos = 0;
8283446Smrj 
8295084Sjohnlev #if defined(__xpv)
8305084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
8315084Sjohnlev 	    console == CONS_HYPERVISOR) {
8325084Sjohnlev 		bcons_putchar_xen(c);
8335084Sjohnlev 		return;
8345084Sjohnlev 	}
8355084Sjohnlev #endif /* __xpv */
8365084Sjohnlev 
8373446Smrj 	if (c == '\t') {
8383446Smrj 		do {
8393446Smrj 			_doputchar(' ');
8403446Smrj 		} while (++bhcharpos % 8);
8413446Smrj 		return;
8423446Smrj 	} else  if (c == '\n' || c == '\r') {
8433446Smrj 		bhcharpos = 0;
8443446Smrj 		_doputchar('\r');
8453446Smrj 		_doputchar(c);
8463446Smrj 		return;
8473446Smrj 	} else if (c == '\b') {
8483446Smrj 		if (bhcharpos)
8493446Smrj 			bhcharpos--;
8503446Smrj 		_doputchar(c);
8513446Smrj 		return;
8523446Smrj 	}
8533446Smrj 
8543446Smrj 	bhcharpos++;
8553446Smrj 	_doputchar(c);
8563446Smrj }
8573446Smrj 
8583446Smrj /*
8593446Smrj  * kernel character input functions
8603446Smrj  */
8613446Smrj int
8623446Smrj bcons_getchar(void)
8633446Smrj {
8645084Sjohnlev #if defined(__xpv)
8655084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
8665084Sjohnlev 	    console == CONS_HYPERVISOR)
8675084Sjohnlev 		return (bcons_getchar_xen());
8685084Sjohnlev #endif /* __xpv */
8695084Sjohnlev 
8703446Smrj 	switch (console) {
8713446Smrj 	case CONS_TTYA:
8723446Smrj 	case CONS_TTYB:
8733446Smrj 		return (serial_getchar());
8743446Smrj 	default:
8753446Smrj 		return (kb_getchar());
8763446Smrj 	}
8773446Smrj }
8783446Smrj 
8793446Smrj #if !defined(_BOOT)
8803446Smrj 
8813446Smrj int
8823446Smrj bcons_ischar(void)
8833446Smrj {
8845084Sjohnlev 
8855084Sjohnlev #if defined(__xpv)
8865084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
8875084Sjohnlev 	    console == CONS_HYPERVISOR)
8885084Sjohnlev 		return (bcons_ischar_xen());
8895084Sjohnlev #endif /* __xpv */
8905084Sjohnlev 
8913446Smrj 	switch (console) {
8923446Smrj 	case CONS_TTYA:
8933446Smrj 	case CONS_TTYB:
8943446Smrj 		return (serial_ischar());
8953446Smrj 	default:
8963446Smrj 		return (kb_ischar());
8973446Smrj 	}
8983446Smrj }
8993446Smrj 
9003446Smrj #endif /* _BOOT */
901