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