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;
6210175SStuart.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
clear_screen(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
screen_putchar(int c)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
serial_init(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 *
find_boot_line_prop(const char * name)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 *
get_mode_value(char * name)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
serial_adjust_prop(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
bcons_init(char * bootstr)5083446Smrj bcons_init(char *bootstr)
5093446Smrj {
5104088Srscott console_value_t *consolep;
5114088Srscott size_t len, cons_len;
5124088Srscott char *cons_str;
513*10574SSherry.Moore@Sun.COM #if !defined(_BOOT)
514*10574SSherry.Moore@Sun.COM static char console_text[] = "text";
515*10574SSherry.Moore@Sun.COM extern int post_fastreboot;
516*10574SSherry.Moore@Sun.COM #endif
5174088Srscott
5183446Smrj boot_line = bootstr;
5193446Smrj console = CONS_INVALID;
5203446Smrj
5215084Sjohnlev #if defined(__xpv)
5225084Sjohnlev bcons_init_xen(bootstr);
5235084Sjohnlev #endif /* __xpv */
5245084Sjohnlev
5254088Srscott cons_str = find_boot_line_prop("console");
5264088Srscott if (cons_str == NULL)
5274088Srscott cons_str = find_boot_line_prop("output-device");
5284088Srscott
529*10574SSherry.Moore@Sun.COM #if !defined(_BOOT)
530*10574SSherry.Moore@Sun.COM if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
531*10574SSherry.Moore@Sun.COM cons_str = console_text;
532*10574SSherry.Moore@Sun.COM #endif
533*10574SSherry.Moore@Sun.COM
5344088Srscott /*
5354088Srscott * Go through the console_devices array trying to match the string
5364088Srscott * we were given. The string on the command line must end with
5374088Srscott * a comma or white space.
5384088Srscott */
5394088Srscott if (cons_str != NULL) {
5404088Srscott cons_len = strlen(cons_str);
5414088Srscott consolep = console_devices;
5424088Srscott for (; consolep->name[0] != '\0'; consolep++) {
5434088Srscott len = strlen(consolep->name);
5444088Srscott if ((len <= cons_len) && ((cons_str[len] == '\0') ||
5454088Srscott (cons_str[len] == ',') || (cons_str[len] == '\'') ||
5464088Srscott (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
5474088Srscott (strncmp(cons_str, consolep->name, len) == 0)) {
5484088Srscott console = consolep->value;
5494088Srscott break;
5504088Srscott }
5514088Srscott }
5524088Srscott }
5533446Smrj
5545084Sjohnlev #if defined(__xpv)
5555084Sjohnlev /*
5565084Sjohnlev * domU's always use the hypervisor regardless of what
5575084Sjohnlev * the console variable may be set to.
5585084Sjohnlev */
5595084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
5605084Sjohnlev console = CONS_HYPERVISOR;
5615084Sjohnlev console_hypervisor_redirect = B_TRUE;
5625084Sjohnlev }
5635084Sjohnlev #endif /* __xpv */
5645084Sjohnlev
5653446Smrj /*
5663446Smrj * If no console device specified, default to text.
5673446Smrj * Remember what was specified for second phase.
5683446Smrj */
5693446Smrj if (console == CONS_INVALID)
5703446Smrj console = CONS_SCREEN_TEXT;
5715084Sjohnlev #if !defined(_BOOT)
5724088Srscott else
5734088Srscott console_set = 1;
5745084Sjohnlev #endif
5755084Sjohnlev
5765084Sjohnlev #if defined(__xpv)
5775084Sjohnlev if (DOMAIN_IS_INITDOMAIN(xen_info)) {
5785084Sjohnlev switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
5795084Sjohnlev case XEN_CONSOLE_COM1:
5805084Sjohnlev console_hypervisor_device = CONS_TTYA;
5815084Sjohnlev break;
5825084Sjohnlev case XEN_CONSOLE_COM2:
5835084Sjohnlev console_hypervisor_device = CONS_TTYB;
5845084Sjohnlev break;
5855084Sjohnlev case XEN_CONSOLE_VGA:
5865084Sjohnlev /*
5875084Sjohnlev * Currently xen doesn't really support
5885084Sjohnlev * keyboard/display console devices.
5895084Sjohnlev * What this setting means is that
5905084Sjohnlev * "vga=keep" has been enabled, which is
5915084Sjohnlev * more of a xen debugging tool that a
5925084Sjohnlev * true console mode. Hence, we're going
5935084Sjohnlev * to ignore this xen "console" setting.
5945084Sjohnlev */
5955084Sjohnlev /*FALLTHROUGH*/
5965084Sjohnlev default:
5975084Sjohnlev console_hypervisor_device = CONS_INVALID;
5985084Sjohnlev }
5995084Sjohnlev }
6005084Sjohnlev
6015084Sjohnlev /*
6025084Sjohnlev * if the hypervisor is using the currently selected serial
6035084Sjohnlev * port then default to using the hypervisor as the console
6045084Sjohnlev * device.
6055084Sjohnlev */
6065084Sjohnlev if (console == console_hypervisor_device) {
6075084Sjohnlev console = CONS_HYPERVISOR;
6085084Sjohnlev console_hypervisor_redirect = B_TRUE;
6095084Sjohnlev }
6105084Sjohnlev #endif /* __xpv */
6113446Smrj
6123446Smrj switch (console) {
6133446Smrj case CONS_TTYA:
6143446Smrj case CONS_TTYB:
6153446Smrj serial_init();
6163446Smrj break;
6173446Smrj
6185084Sjohnlev case CONS_HYPERVISOR:
6195084Sjohnlev break;
6205084Sjohnlev
6214088Srscott #if !defined(_BOOT)
6224088Srscott case CONS_USBSER:
6234088Srscott /*
6244088Srscott * We can't do anything with the usb serial
6254088Srscott * until we have memory management.
6264088Srscott */
6274088Srscott break;
6284088Srscott #endif
6297701SJan.Setje-Eilers@Sun.COM case CONS_SCREEN_GRAPHICS:
6307701SJan.Setje-Eilers@Sun.COM kb_init();
6317701SJan.Setje-Eilers@Sun.COM break;
6323446Smrj case CONS_SCREEN_TEXT:
6333446Smrj default:
6343446Smrj #if defined(_BOOT)
6355084Sjohnlev clear_screen(); /* clears the grub or xen screen */
6365084Sjohnlev #endif /* _BOOT */
6373446Smrj kb_init();
6383446Smrj break;
6393446Smrj }
6403446Smrj boot_line = NULL;
6413446Smrj }
6423446Smrj
6435084Sjohnlev #if !defined(_BOOT)
6443446Smrj /*
6453446Smrj * 2nd part of console initialization.
6463446Smrj * In the kernel (ie. fakebop), this can be used only to switch to
6473446Smrj * using a serial port instead of screen based on the contents
6483446Smrj * of the bootenv.rc file.
6493446Smrj */
6503446Smrj /*ARGSUSED*/
6513446Smrj void
bcons_init2(char * inputdev,char * outputdev,char * consoledev)6523446Smrj bcons_init2(char *inputdev, char *outputdev, char *consoledev)
6533446Smrj {
6543446Smrj int cons = CONS_INVALID;
6554088Srscott char *devnames[] = { consoledev, outputdev, inputdev, NULL };
6564088Srscott console_value_t *consolep;
6574088Srscott int i;
658*10574SSherry.Moore@Sun.COM extern int post_fastreboot;
659*10574SSherry.Moore@Sun.COM
660*10574SSherry.Moore@Sun.COM if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
661*10574SSherry.Moore@Sun.COM console = CONS_SCREEN_TEXT;
6623446Smrj
66310092SEnrico.Perla@Sun.COM if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
6644088Srscott if (console_set) {
6654088Srscott /*
6664088Srscott * If the console was set on the command line,
6674088Srscott * but the ttyX-mode was not, we only need to
6684088Srscott * check bootenv.rc for that setting.
6694088Srscott */
6704088Srscott if ((!console_mode_set) &&
6714088Srscott (console == CONS_TTYA || console == CONS_TTYB))
6724088Srscott serial_init();
6734088Srscott return;
6744088Srscott }
6753446Smrj
6764088Srscott for (i = 0; devnames[i] != NULL; i++) {
6774088Srscott consolep = console_devices;
6784088Srscott for (; consolep->name[0] != '\0'; consolep++) {
6794088Srscott if (strcmp(devnames[i], consolep->name) == 0) {
6804088Srscott cons = consolep->value;
6814088Srscott }
6824088Srscott }
6834088Srscott if (cons != CONS_INVALID)
6844088Srscott break;
6854088Srscott }
6863446Smrj
6875084Sjohnlev #if defined(__xpv)
6885084Sjohnlev /*
6895084Sjohnlev * if the hypervisor is using the currently selected console
6905084Sjohnlev * device then default to using the hypervisor as the console
6915084Sjohnlev * device.
6925084Sjohnlev */
6935084Sjohnlev if (cons == console_hypervisor_device) {
6945084Sjohnlev cons = CONS_HYPERVISOR;
6955084Sjohnlev console_hypervisor_redirect = B_TRUE;
6965084Sjohnlev }
6975084Sjohnlev #endif /* __xpv */
6985084Sjohnlev
6995084Sjohnlev if ((cons == CONS_INVALID) || (cons == console)) {
7005084Sjohnlev /*
7015084Sjohnlev * we're sticking with whatever the current setting is
7025084Sjohnlev */
7034088Srscott return;
7045084Sjohnlev }
7053446Smrj
7064088Srscott console = cons;
7074088Srscott if (cons == CONS_TTYA || cons == CONS_TTYB) {
7084088Srscott serial_init();
7094088Srscott return;
7104088Srscott }
71110092SEnrico.Perla@Sun.COM } else {
71210092SEnrico.Perla@Sun.COM /*
71310092SEnrico.Perla@Sun.COM * USB serial and GRAPHICS console
71410092SEnrico.Perla@Sun.COM * we just collect data into a buffer
71510092SEnrico.Perla@Sun.COM */
7168960SJan.Setje-Eilers@Sun.COM extern void *defcons_init(size_t);
7178960SJan.Setje-Eilers@Sun.COM defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
7183446Smrj }
7195084Sjohnlev }
7205084Sjohnlev
7215084Sjohnlev #if defined(__xpv)
7225084Sjohnlev boolean_t
bcons_hypervisor_redirect(void)7235084Sjohnlev bcons_hypervisor_redirect(void)
7245084Sjohnlev {
7255084Sjohnlev return (console_hypervisor_redirect);
7263446Smrj }
7273446Smrj
7285084Sjohnlev void
bcons_device_change(int new_console)7295084Sjohnlev bcons_device_change(int new_console)
7305084Sjohnlev {
7315084Sjohnlev if (new_console < CONS_MIN || new_console > CONS_MAX)
7325084Sjohnlev return;
7335084Sjohnlev
7345084Sjohnlev /*
7355084Sjohnlev * If we are asked to switch the console to the hypervisor, that
7365084Sjohnlev * really means to switch the console to whichever device the
7375084Sjohnlev * hypervisor is/was using.
7385084Sjohnlev */
7395084Sjohnlev if (new_console == CONS_HYPERVISOR)
7405084Sjohnlev new_console = console_hypervisor_device;
7415084Sjohnlev
7425084Sjohnlev console = new_console;
7435084Sjohnlev
7445084Sjohnlev if (new_console == CONS_TTYA || new_console == CONS_TTYB)
7455084Sjohnlev serial_init();
7465084Sjohnlev }
7475084Sjohnlev #endif /* __xpv */
7485084Sjohnlev
7493446Smrj static void
defcons_putchar(int c)7508960SJan.Setje-Eilers@Sun.COM defcons_putchar(int c)
7513446Smrj {
75210092SEnrico.Perla@Sun.COM if (defcons_buf != NULL &&
75310092SEnrico.Perla@Sun.COM defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
7548960SJan.Setje-Eilers@Sun.COM *defcons_cur++ = c;
7559462SJan.Setje-Eilers@Sun.COM *defcons_cur = 0;
7569462SJan.Setje-Eilers@Sun.COM }
7573446Smrj }
7583446Smrj #endif /* _BOOT */
7593446Smrj
7603446Smrj static void
serial_putchar(int c)7613446Smrj serial_putchar(int c)
7623446Smrj {
7633446Smrj int checks = 10000;
7643446Smrj
7653446Smrj while (((inb(port + LSR) & XHRE) == 0) && checks--)
7663446Smrj ;
7673446Smrj outb(port + DAT, (char)c);
7683446Smrj }
7693446Smrj
7703446Smrj static int
serial_getchar(void)7713446Smrj serial_getchar(void)
7723446Smrj {
7733446Smrj uchar_t lsr;
7743446Smrj
7753446Smrj while (serial_ischar() == 0)
7763446Smrj ;
7773446Smrj
7783446Smrj lsr = inb(port + LSR);
7793446Smrj if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
7803446Smrj SERIAL_PARITY | SERIAL_OVERRUN)) {
7813446Smrj if (lsr & SERIAL_OVERRUN) {
7823446Smrj return (inb(port + DAT));
7833446Smrj } else {
7843446Smrj /* Toss the garbage */
7853446Smrj (void) inb(port + DAT);
7863446Smrj return (0);
7873446Smrj }
7883446Smrj }
7893446Smrj return (inb(port + DAT));
7903446Smrj }
7913446Smrj
7923446Smrj static int
serial_ischar(void)7933446Smrj serial_ischar(void)
7943446Smrj {
7953446Smrj return (inb(port + LSR) & RCA);
7963446Smrj }
7973446Smrj
7983446Smrj static void
_doputchar(int c)7993446Smrj _doputchar(int c)
8003446Smrj {
8013446Smrj switch (console) {
8023446Smrj case CONS_TTYA:
8033446Smrj case CONS_TTYB:
8043446Smrj serial_putchar(c);
8053446Smrj return;
8063446Smrj case CONS_SCREEN_TEXT:
8073446Smrj screen_putchar(c);
8083446Smrj return;
8097701SJan.Setje-Eilers@Sun.COM case CONS_SCREEN_GRAPHICS:
8103446Smrj #if !defined(_BOOT)
8113446Smrj case CONS_USBSER:
8128960SJan.Setje-Eilers@Sun.COM defcons_putchar(c);
8138960SJan.Setje-Eilers@Sun.COM #endif /* _BOOT */
8143446Smrj return;
8153446Smrj }
8163446Smrj }
8173446Smrj
8183446Smrj void
bcons_putchar(int c)8193446Smrj bcons_putchar(int c)
8203446Smrj {
8213446Smrj static int bhcharpos = 0;
8223446Smrj
8235084Sjohnlev #if defined(__xpv)
8245084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
8255084Sjohnlev console == CONS_HYPERVISOR) {
8265084Sjohnlev bcons_putchar_xen(c);
8275084Sjohnlev return;
8285084Sjohnlev }
8295084Sjohnlev #endif /* __xpv */
8305084Sjohnlev
8313446Smrj if (c == '\t') {
8323446Smrj do {
8333446Smrj _doputchar(' ');
8343446Smrj } while (++bhcharpos % 8);
8353446Smrj return;
8363446Smrj } else if (c == '\n' || c == '\r') {
8373446Smrj bhcharpos = 0;
8383446Smrj _doputchar('\r');
8393446Smrj _doputchar(c);
8403446Smrj return;
8413446Smrj } else if (c == '\b') {
8423446Smrj if (bhcharpos)
8433446Smrj bhcharpos--;
8443446Smrj _doputchar(c);
8453446Smrj return;
8463446Smrj }
8473446Smrj
8483446Smrj bhcharpos++;
8493446Smrj _doputchar(c);
8503446Smrj }
8513446Smrj
8523446Smrj /*
8533446Smrj * kernel character input functions
8543446Smrj */
8553446Smrj int
bcons_getchar(void)8563446Smrj bcons_getchar(void)
8573446Smrj {
8585084Sjohnlev #if defined(__xpv)
8595084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
8605084Sjohnlev console == CONS_HYPERVISOR)
8615084Sjohnlev return (bcons_getchar_xen());
8625084Sjohnlev #endif /* __xpv */
8635084Sjohnlev
8643446Smrj switch (console) {
8653446Smrj case CONS_TTYA:
8663446Smrj case CONS_TTYB:
8673446Smrj return (serial_getchar());
8683446Smrj default:
8693446Smrj return (kb_getchar());
8703446Smrj }
8713446Smrj }
8723446Smrj
8733446Smrj #if !defined(_BOOT)
8743446Smrj
8753446Smrj int
bcons_ischar(void)8763446Smrj bcons_ischar(void)
8773446Smrj {
8785084Sjohnlev
8795084Sjohnlev #if defined(__xpv)
8805084Sjohnlev if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
8815084Sjohnlev console == CONS_HYPERVISOR)
8825084Sjohnlev return (bcons_ischar_xen());
8835084Sjohnlev #endif /* __xpv */
8845084Sjohnlev
8853446Smrj switch (console) {
8863446Smrj case CONS_TTYA:
8873446Smrj case CONS_TTYB:
8883446Smrj return (serial_ischar());
8893446Smrj default:
8903446Smrj return (kb_ischar());
8913446Smrj }
8923446Smrj }
8933446Smrj
8943446Smrj #endif /* _BOOT */
895