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