/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2012 Gary Mills * Copyright 2020 Joyent, Inc. * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Boot console support. Most of the file is shared between dboot, and the * early kernel / fakebop. */ #include #include #include #include #include #include #include #include #include #if defined(__xpv) #include #endif /* __xpv */ #include "boot_console_impl.h" #include "boot_serial.h" #if defined(_BOOT) #include #include #else /* _BOOT */ #include #if defined(__xpv) #include #endif /* __xpv */ static char *defcons_buf; static char *defcons_cur; #endif /* _BOOT */ #if defined(__xpv) extern void bcons_init_xen(char *); extern void bcons_putchar_xen(int); extern int bcons_getchar_xen(void); extern int bcons_ischar_xen(void); #endif /* __xpv */ fb_info_t fb_info; static bcons_dev_t bcons_dev; /* Device callbacks */ static int console = CONS_SCREEN_TEXT; static int diag = CONS_INVALID; static int tty_num = 0; static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8}; static char *boot_line; static struct boot_env { char *be_env; /* ends with double ascii nul */ size_t be_size; /* size of the environment, including nul */ } boot_env; /* * Simple console terminal emulator for early boot. * We need this to support kmdb, all other console output is supposed * to be simple text output. */ typedef enum btem_state_type { A_STATE_START, A_STATE_ESC, A_STATE_CSI, A_STATE_CSI_QMARK, A_STATE_CSI_EQUAL } btem_state_type_t; #define BTEM_MAXPARAMS 5 typedef struct btem_state { btem_state_type_t btem_state; boolean_t btem_gotparam; int btem_curparam; int btem_paramval; int btem_params[BTEM_MAXPARAMS]; } btem_state_t; static btem_state_t boot_tem; static int serial_ischar(void); static int serial_getchar(void); static void serial_putchar(int); static void serial_adjust_prop(void); static void defcons_putchar(int); #if !defined(_BOOT) static boolean_t bootprop_set_tty_mode; #endif #if defined(__xpv) static int console_hypervisor_redirect = B_FALSE; static int console_hypervisor_device = CONS_INVALID; static int console_hypervisor_tty_num = 0; /* Obtain the hypervisor console type */ int console_hypervisor_dev_type(int *tnum) { if (tnum != NULL) *tnum = console_hypervisor_tty_num; return (console_hypervisor_device); } #endif /* __xpv */ static int port; static void serial_init(void) { port = tty_addr[tty_num]; outb(port + ISR, 0x20); if (inb(port + ISR) & 0x20) { /* * 82510 chip is present */ outb(port + DAT+7, 0x04); /* clear status */ outb(port + ISR, 0x40); /* set to bank 2 */ outb(port + MCR, 0x08); /* IMD */ outb(port + DAT, 0x21); /* FMD */ outb(port + ISR, 0x00); /* set to bank 0 */ } else { /* * set the UART in FIFO mode if it has FIFO buffers. * use 16550 fifo reset sequence specified in NS * application note. disable fifos until chip is * initialized. */ outb(port + FIFOR, 0x00); /* clear */ outb(port + FIFOR, FIFO_ON); /* enable */ outb(port + FIFOR, FIFO_ON|FIFORXFLSH); /* reset */ outb(port + FIFOR, FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80); if ((inb(port + ISR) & 0xc0) != 0xc0) { /* * no fifo buffers so disable fifos. * this is true for 8250's */ outb(port + FIFOR, 0x00); } } /* disable interrupts */ outb(port + ICR, 0); #if !defined(_BOOT) if (IN_XPV_PANIC()) return; #endif /* adjust setting based on tty properties */ serial_adjust_prop(); } /* Advance str pointer past white space */ #define EAT_WHITE_SPACE(str) { \ while ((*str != '\0') && ISSPACE(*str)) \ str++; \ } /* * boot_line is set when we call here. Search it for the argument name, * and if found, return a pointer to it. */ static char * find_boot_line_prop(const char *name) { char *ptr; char *ret = NULL; char end_char; size_t len; if (boot_line == NULL) return (NULL); len = strlen(name); /* * We have two nested loops here: the outer loop discards all options * except -B, and the inner loop parses the -B options looking for * the one we're interested in. */ for (ptr = boot_line; *ptr != '\0'; ptr++) { EAT_WHITE_SPACE(ptr); if (*ptr == '-') { ptr++; while ((*ptr != '\0') && (*ptr != 'B') && !ISSPACE(*ptr)) ptr++; if (*ptr == '\0') goto out; else if (*ptr != 'B') continue; } else { while ((*ptr != '\0') && !ISSPACE(*ptr)) ptr++; if (*ptr == '\0') goto out; continue; } do { ptr++; EAT_WHITE_SPACE(ptr); if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) { ptr += len + 1; if ((*ptr == '\'') || (*ptr == '"')) { ret = ptr + 1; end_char = *ptr; ptr++; } else { ret = ptr; end_char = ','; } goto consume_property; } /* * We have a property, and it's not the one we're * interested in. Skip the property name. A name * can end with '=', a comma, or white space. */ while ((*ptr != '\0') && (*ptr != '=') && (*ptr != ',') && (!ISSPACE(*ptr))) ptr++; /* * We only want to go through the rest of the inner * loop if we have a comma. If we have a property * name without a value, either continue or break. */ if (*ptr == '\0') goto out; else if (*ptr == ',') continue; else if (ISSPACE(*ptr)) break; ptr++; /* * Is the property quoted? */ if ((*ptr == '\'') || (*ptr == '"')) { end_char = *ptr; ptr++; } else { /* * Not quoted, so the string ends at a comma * or at white space. Deal with white space * later. */ end_char = ','; } /* * Now, we can ignore any characters until we find * end_char. */ consume_property: for (; (*ptr != '\0') && (*ptr != end_char); ptr++) { if ((end_char == ',') && ISSPACE(*ptr)) break; } if (*ptr && (*ptr != ',') && !ISSPACE(*ptr)) ptr++; } while (*ptr == ','); } out: return (ret); } /* * Find prop from boot env module. The data in module is list of C strings * name=value, the list is terminated by double nul. */ static const char * find_boot_env_prop(const char *name) { char *ptr; size_t len; uintptr_t size; if (boot_env.be_env == NULL) return (NULL); ptr = boot_env.be_env; len = strlen(name); /* * Make sure we have at least len + 2 bytes in the environment. * We are looking for name=value\0 constructs, and the environment * itself is terminated by '\0'. */ if (boot_env.be_size < len + 2) return (NULL); do { if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) { ptr += len + 1; return (ptr); } /* find the first '\0' */ while (*ptr != '\0') { ptr++; size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env; if (size > boot_env.be_size) return (NULL); } ptr++; /* If the remainder is shorter than name + 2, get out. */ size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env; if (boot_env.be_size - size < len + 2) return (NULL); } while (*ptr != '\0'); return (NULL); } /* * Get prop value from either command line or boot environment. * We always check kernel command line first, as this will keep the * functionality and will allow user to override the values in environment. */ const char * find_boot_prop(const char *name) { const char *value = find_boot_line_prop(name); if (value == NULL) value = find_boot_env_prop(name); return (value); } #define MATCHES(p, pat) \ (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0) #define SKIP(p, c) \ while (*(p) != 0 && *p != (c)) \ ++(p); \ if (*(p) == (c)) \ ++(p); /* * find a tty mode property either from cmdline or from boot properties */ static const char * get_mode_value(char *name) { /* * when specified on boot line it looks like "name" "=".... */ if (boot_line != NULL) { return (find_boot_prop(name)); } #if defined(_BOOT) return (NULL); #else /* * if we're running in the full kernel we check the bootenv.rc settings */ { static char propval[20]; propval[0] = 0; if (do_bsys_getproplen(NULL, name) <= 0) return (NULL); (void) do_bsys_getprop(NULL, name, propval); return (propval); } #endif } /* * adjust serial port based on properties * These come either from the cmdline or from boot properties. */ static void serial_adjust_prop(void) { char propname[20]; const char *propval; const char *p; ulong_t baud; uchar_t lcr = 0; uchar_t mcr = DTR | RTS; (void) strcpy(propname, "ttyX-mode"); propname[3] = 'a' + tty_num; propval = get_mode_value(propname); #if !defined(_BOOT) if (propval != NULL) bootprop_set_tty_mode = B_TRUE; #endif if (propval == NULL) propval = "9600,8,n,1,-"; /* property is of the form: "9600,8,n,1,-" */ p = propval; if (MATCHES(p, "110,")) baud = ASY110; else if (MATCHES(p, "150,")) baud = ASY150; else if (MATCHES(p, "300,")) baud = ASY300; else if (MATCHES(p, "600,")) baud = ASY600; else if (MATCHES(p, "1200,")) baud = ASY1200; else if (MATCHES(p, "2400,")) baud = ASY2400; else if (MATCHES(p, "4800,")) baud = ASY4800; else if (MATCHES(p, "19200,")) baud = ASY19200; else if (MATCHES(p, "38400,")) baud = ASY38400; else if (MATCHES(p, "57600,")) baud = ASY57600; else if (MATCHES(p, "115200,")) baud = ASY115200; else { baud = ASY9600; SKIP(p, ','); } outb(port + LCR, DLAB); outb(port + DAT + DLL, baud & 0xff); outb(port + DAT + DLH, (baud >> 8) & 0xff); switch (*p) { case '5': lcr |= BITS5; ++p; break; case '6': lcr |= BITS6; ++p; break; case '7': lcr |= BITS7; ++p; break; case '8': ++p; /* FALLTHROUGH */ default: lcr |= BITS8; break; } SKIP(p, ','); switch (*p) { case 'n': lcr |= PARITY_NONE; ++p; break; case 'o': lcr |= PARITY_ODD; ++p; break; case 'e': ++p; /* FALLTHROUGH */ default: lcr |= PARITY_EVEN; break; } SKIP(p, ','); switch (*p) { case '1': /* STOP1 is 0 */ ++p; break; default: lcr |= STOP2; break; } /* set parity bits */ outb(port + LCR, lcr); (void) strcpy(propname, "ttyX-rts-dtr-off"); propname[3] = 'a' + tty_num; propval = get_mode_value(propname); if (propval == NULL) propval = "false"; if (propval[0] != 'f' && propval[0] != 'F') mcr = 0; /* set modem control bits */ outb(port + MCR, mcr | OUT2); } /* Obtain the console type */ int boot_console_type(int *tnum) { if (tnum != NULL) *tnum = tty_num; return (console); } /* * A structure to map console names to values. */ typedef struct { char *name; int value; } console_value_t; console_value_t console_devices[] = { { "ttya", CONS_TTY }, /* 0 */ { "ttyb", CONS_TTY }, /* 1 */ { "ttyc", CONS_TTY }, /* 2 */ { "ttyd", CONS_TTY }, /* 3 */ { "text", CONS_SCREEN_TEXT }, { "graphics", CONS_SCREEN_GRAPHICS }, #if defined(__xpv) { "hypervisor", CONS_HYPERVISOR }, #endif #if !defined(_BOOT) { "usb-serial", CONS_USBSER }, #endif { NULL, CONS_INVALID } }; static void bcons_init_env(struct xboot_info *xbi) { uint32_t i; struct boot_modules *modules; modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules; for (i = 0; i < xbi->bi_module_cnt; i++) { if (modules[i].bm_type == BMT_ENV) break; } if (i == xbi->bi_module_cnt) return; boot_env.be_env = (char *)(uintptr_t)modules[i].bm_addr; boot_env.be_size = modules[i].bm_size; } int boot_fb(struct xboot_info *xbi, int console) { if (xbi_fb_init(xbi, &bcons_dev) == B_FALSE) return (console); /* FB address is not set, fall back to serial terminal. */ if (fb_info.paddr == 0) return (CONS_TTY); fb_info.terminal.x = VGA_TEXT_COLS; fb_info.terminal.y = VGA_TEXT_ROWS; boot_fb_init(CONS_FRAMEBUFFER); if (console == CONS_SCREEN_TEXT) return (CONS_FRAMEBUFFER); return (console); } /* * TODO. * quick and dirty local atoi. Perhaps should build with strtol, but * dboot & early boot mix does overcomplicate things much. * Stolen from libc anyhow. */ static int atoi(const char *p) { int n, c, neg = 0; unsigned char *up = (unsigned char *)p; if (!isdigit(c = *up)) { while (isspace(c)) c = *++up; switch (c) { case '-': neg++; /* FALLTHROUGH */ case '+': c = *++up; } if (!isdigit(c)) return (0); } for (n = '0' - c; isdigit(c = *++up); ) { n *= 10; /* two steps to avoid unnecessary overflow */ n += '0' - c; /* accum neg to avoid surprises at MAX */ } return (neg ? n : -n); } static void bcons_init_fb(void) { const char *propval; int intval; /* initialize with explicit default values */ fb_info.fg_color = CONS_COLOR; fb_info.bg_color = 0; fb_info.inverse = B_FALSE; fb_info.inverse_screen = B_FALSE; /* color values are 0 - 255 */ propval = find_boot_prop("tem.fg_color"); if (propval != NULL) { intval = atoi(propval); if (intval >= 0 && intval <= 255) fb_info.fg_color = intval; } /* color values are 0 - 255 */ propval = find_boot_prop("tem.bg_color"); if (propval != NULL && ISDIGIT(*propval)) { intval = atoi(propval); if (intval >= 0 && intval <= 255) fb_info.bg_color = intval; } /* get inverses. allow 0, 1, true, false */ propval = find_boot_prop("tem.inverse"); if (propval != NULL) { if (*propval == '1' || MATCHES(propval, "true")) fb_info.inverse = B_TRUE; } propval = find_boot_prop("tem.inverse-screen"); if (propval != NULL) { if (*propval == '1' || MATCHES(propval, "true")) fb_info.inverse_screen = B_TRUE; } #if defined(_BOOT) /* * Load cursor position from bootloader only in dboot, * dboot will pass cursor position to kernel via xboot info. */ propval = find_boot_prop("tem.cursor.row"); if (propval != NULL) { intval = atoi(propval); if (intval >= 0 && intval <= 0xFFFF) fb_info.cursor.pos.y = intval; } propval = find_boot_prop("tem.cursor.col"); if (propval != NULL) { intval = atoi(propval); if (intval >= 0 && intval <= 0xFFFF) fb_info.cursor.pos.x = intval; } #endif } /* * Go through the known console device names trying to match the string we were * given. The string on the command line must end with a comma or white space. * * For convenience, we provide the caller with an integer index for the CONS_TTY * case. */ static int lookup_console_device(const char *cons_str, int *indexp) { int n, cons; size_t len, cons_len; console_value_t *consolep; cons = CONS_INVALID; if (cons_str != NULL) { cons_len = strlen(cons_str); for (n = 0; console_devices[n].name != NULL; n++) { consolep = &console_devices[n]; len = strlen(consolep->name); if ((len <= cons_len) && ((cons_str[len] == '\0') || (cons_str[len] == ',') || (cons_str[len] == '\'') || (cons_str[len] == '"') || ISSPACE(cons_str[len])) && (strncmp(cons_str, consolep->name, len) == 0)) { cons = consolep->value; if (cons == CONS_TTY) *indexp = n; break; } } } return (cons); } void bcons_init(struct xboot_info *xbi) { const char *cons_str; #if !defined(_BOOT) static char console_text[] = "text"; extern int post_fastreboot; #endif if (xbi == NULL) { /* This is very early dboot console, set up ttya. */ console = CONS_TTY; serial_init(); return; } /* Set up data to fetch properties from commad line and boot env. */ boot_line = (char *)(uintptr_t)xbi->bi_cmdline; bcons_init_env(xbi); console = CONS_INVALID; /* set up initial fb_info */ bcons_init_fb(); #if defined(__xpv) bcons_init_xen(boot_line); #endif /* __xpv */ /* * First check for diag-device. */ cons_str = find_boot_prop("diag-device"); if (cons_str != NULL) diag = lookup_console_device(cons_str, &tty_num); cons_str = find_boot_prop("console"); if (cons_str == NULL) cons_str = find_boot_prop("output-device"); #if !defined(_BOOT) if (post_fastreboot && strcmp(cons_str, "graphics") == 0) cons_str = console_text; #endif if (cons_str != NULL) console = lookup_console_device(cons_str, &tty_num); #if defined(__xpv) /* * domU's always use the hypervisor regardless of what * the console variable may be set to. */ if (!DOMAIN_IS_INITDOMAIN(xen_info)) { console = CONS_HYPERVISOR; console_hypervisor_redirect = B_TRUE; } #endif /* __xpv */ if (console == CONS_INVALID) console = CONS_SCREEN_TEXT; #if defined(__xpv) if (DOMAIN_IS_INITDOMAIN(xen_info)) { switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) { case XEN_CONSOLE_COM1: case XEN_CONSOLE_COM2: console_hypervisor_device = CONS_TTY; console_hypervisor_tty_num = tty_num; break; case XEN_CONSOLE_VGA: /* * Currently xen doesn't really support * keyboard/display console devices. * What this setting means is that * "vga=keep" has been enabled, which is * more of a xen debugging tool that a * true console mode. Hence, we're going * to ignore this xen "console" setting. */ /*FALLTHROUGH*/ default: console_hypervisor_device = CONS_INVALID; } } /* * if the hypervisor is using the currently selected serial * port then default to using the hypervisor as the console * device. */ if (console == console_hypervisor_device) { console = CONS_HYPERVISOR; console_hypervisor_redirect = B_TRUE; } #endif /* __xpv */ /* make sure the FB is set up if present */ console = boot_fb(xbi, console); switch (console) { case CONS_TTY: serial_init(); break; case CONS_HYPERVISOR: break; #if !defined(_BOOT) case CONS_USBSER: /* * We can't do anything with the usb serial * until we have memory management. */ break; #endif case CONS_SCREEN_GRAPHICS: kb_init(); break; case CONS_SCREEN_TEXT: boot_vga_init(&bcons_dev); /* Fall through */ default: kb_init(); break; } /* * Initialize diag device unless already done. */ switch (diag) { case CONS_TTY: if (console != CONS_TTY) serial_init(); break; case CONS_SCREEN_GRAPHICS: case CONS_SCREEN_TEXT: if (console != CONS_SCREEN_GRAPHICS && console != CONS_SCREEN_TEXT) kb_init(); break; default: break; } } static void serial_putchar(int c) { int checks = 10000; while (((inb(port + LSR) & XHRE) == 0) && checks--) ; outb(port + DAT, (char)c); } static int serial_getchar(void) { uchar_t lsr; while (serial_ischar() == 0) ; lsr = inb(port + LSR); if (lsr & (SERIAL_BREAK | SERIAL_FRAME | SERIAL_PARITY | SERIAL_OVERRUN)) { if (lsr & SERIAL_OVERRUN) { return (inb(port + DAT)); } else { /* Toss the garbage */ (void) inb(port + DAT); return (0); } } return (inb(port + DAT)); } static int serial_ischar(void) { return (inb(port + LSR) & RCA); } static void btem_control(btem_state_t *btem, int c) { int y, rows, cols; rows = fb_info.cursor.pos.y; cols = fb_info.cursor.pos.x; btem->btem_state = A_STATE_START; switch (c) { case A_BS: bcons_dev.bd_setpos(rows, cols - 1); break; case A_HT: cols += 8 - (cols % 8); if (cols >= fb_info.terminal.x) cols = fb_info.terminal.x - 1; bcons_dev.bd_setpos(rows, cols); break; case A_CR: bcons_dev.bd_setpos(rows, 0); break; case A_FF: for (y = 0; y < fb_info.terminal.y; y++) { bcons_dev.bd_setpos(y, 0); bcons_dev.bd_eraseline(); } bcons_dev.bd_setpos(0, 0); break; case A_ESC: btem->btem_state = A_STATE_ESC; break; default: bcons_dev.bd_putchar(c); break; } } /* * if parameters [0..count - 1] are not set, set them to the value * of newparam. */ static void btem_setparam(btem_state_t *btem, int count, int newparam) { int i; for (i = 0; i < count; i++) { if (btem->btem_params[i] == -1) btem->btem_params[i] = newparam; } } static void btem_chkparam(btem_state_t *btem, int c) { int rows, cols; rows = fb_info.cursor.pos.y; cols = fb_info.cursor.pos.x; switch (c) { case '@': /* insert char */ btem_setparam(btem, 1, 1); bcons_dev.bd_shift(btem->btem_params[0]); break; case 'A': /* cursor up */ btem_setparam(btem, 1, 1); bcons_dev.bd_setpos(rows - btem->btem_params[0], cols); break; case 'B': /* cursor down */ btem_setparam(btem, 1, 1); bcons_dev.bd_setpos(rows + btem->btem_params[0], cols); break; case 'C': /* cursor right */ btem_setparam(btem, 1, 1); bcons_dev.bd_setpos(rows, cols + btem->btem_params[0]); break; case 'D': /* cursor left */ btem_setparam(btem, 1, 1); bcons_dev.bd_setpos(rows, cols - btem->btem_params[0]); break; case 'K': bcons_dev.bd_eraseline(); break; default: /* bcons_dev.bd_putchar(c); */ break; } btem->btem_state = A_STATE_START; } static void btem_getparams(btem_state_t *btem, int c) { if (isdigit(c)) { btem->btem_paramval = btem->btem_paramval * 10 + c - '0'; btem->btem_gotparam = B_TRUE; return; } if (btem->btem_curparam < BTEM_MAXPARAMS) { if (btem->btem_gotparam == B_TRUE) { btem->btem_params[btem->btem_curparam] = btem->btem_paramval; } btem->btem_curparam++; } if (c == ';') { /* Restart parameter search */ btem->btem_gotparam = B_FALSE; btem->btem_paramval = 0; } else { btem_chkparam(btem, c); } } /* Simple boot terminal parser. */ static void btem_parse(btem_state_t *btem, int c) { int i; /* Normal state? */ if (btem->btem_state == A_STATE_START) { if (c == A_CSI || c < ' ') btem_control(btem, c); else bcons_dev.bd_putchar(c); return; } /* In sequence */ if (btem->btem_state != A_STATE_ESC) { btem_getparams(btem, c); return; } /* Previous char was */ switch (c) { case '[': btem->btem_curparam = 0; btem->btem_paramval = 0; btem->btem_gotparam = B_FALSE; /* clear the parameters */ for (i = 0; i < BTEM_MAXPARAMS; i++) btem->btem_params[i] = -1; btem->btem_state = A_STATE_CSI; return; case 'Q': /* Q */ case 'C': /* C */ btem->btem_state = A_STATE_START; return; default: btem->btem_state = A_STATE_START; break; } if (c < ' ') btem_control(btem, c); else bcons_dev.bd_putchar(c); } static void _doputchar(int device, int c) { switch (device) { case CONS_TTY: serial_putchar(c); return; case CONS_SCREEN_TEXT: case CONS_FRAMEBUFFER: bcons_dev.bd_cursor(B_FALSE); btem_parse(&boot_tem, c); bcons_dev.bd_cursor(B_TRUE); return; case CONS_SCREEN_GRAPHICS: #if !defined(_BOOT) case CONS_USBSER: defcons_putchar(c); #endif /* _BOOT */ default: return; } } void bcons_putchar(int c) { #if defined(__xpv) if (!DOMAIN_IS_INITDOMAIN(xen_info) || console == CONS_HYPERVISOR) { bcons_putchar_xen(c); return; } #endif /* __xpv */ if (c == '\n') { _doputchar(console, '\r'); if (diag != console) _doputchar(diag, '\r'); } _doputchar(console, c); if (diag != console) _doputchar(diag, c); } /* * kernel character input functions */ int bcons_getchar(void) { #if defined(__xpv) if (!DOMAIN_IS_INITDOMAIN(xen_info) || console == CONS_HYPERVISOR) return (bcons_getchar_xen()); #endif /* __xpv */ for (;;) { if (console == CONS_TTY || diag == CONS_TTY) { if (serial_ischar()) return (serial_getchar()); } if (console != CONS_INVALID || diag != CONS_INVALID) { if (kb_ischar()) return (kb_getchar()); } } } /* * Nothing below is used by dboot. */ #if !defined(_BOOT) int bcons_ischar(void) { int c = 0; #if defined(__xpv) if (!DOMAIN_IS_INITDOMAIN(xen_info) || console == CONS_HYPERVISOR) return (bcons_ischar_xen()); #endif /* __xpv */ switch (console) { case CONS_TTY: c = serial_ischar(); break; case CONS_INVALID: break; default: c = kb_ischar(); } if (c != 0) return (c); switch (diag) { case CONS_TTY: c = serial_ischar(); break; case CONS_INVALID: break; default: c = kb_ischar(); } return (c); } /* * 2nd part of console initialization: we've now processed bootenv.rc; update * console settings as appropriate. This only really processes serial console * modifications. */ void bcons_post_bootenvrc(char *inputdev, char *outputdev, char *consoledev) { int cons = CONS_INVALID; int ttyn; char *devnames[] = { consoledev, outputdev, inputdev, NULL }; console_value_t *consolep; int i; extern int post_fastreboot; ttyn = 0; if (post_fastreboot && console == CONS_SCREEN_GRAPHICS) console = CONS_SCREEN_TEXT; /* * USB serial and GRAPHICS console: we just collect data into a buffer. */ if (console == CONS_USBSER || console == CONS_SCREEN_GRAPHICS) { extern void *defcons_init(size_t); defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE); return; } for (i = 0; devnames[i] != NULL; i++) { cons = lookup_console_device(devnames[i], &ttyn); if (cons != CONS_INVALID) break; } if (cons == CONS_INVALID) { /* * No console change, but let's see if bootenv.rc had a mode * setting we should apply. */ if (console == CONS_TTY && !bootprop_set_tty_mode) serial_init(); return; } #if defined(__xpv) /* * if the hypervisor is using the currently selected console device then * default to using the hypervisor as the console device. */ if (cons == console_hypervisor_device) { cons = CONS_HYPERVISOR; console_hypervisor_redirect = B_TRUE; } #endif /* __xpv */ console = cons; if (console == CONS_TTY) { tty_num = ttyn; serial_init(); } } #if defined(__xpv) boolean_t bcons_hypervisor_redirect(void) { return (console_hypervisor_redirect); } void bcons_device_change(int new_console) { if (new_console < CONS_MIN || new_console > CONS_MAX) return; /* * If we are asked to switch the console to the hypervisor, that * really means to switch the console to whichever device the * hypervisor is/was using. */ if (new_console == CONS_HYPERVISOR) new_console = console_hypervisor_device; console = new_console; if (new_console == CONS_TTY) { tty_num = console_hypervisor_tty_num; serial_init(); } } #endif /* __xpv */ static void defcons_putchar(int c) { if (defcons_buf != NULL && defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) { *defcons_cur++ = c; *defcons_cur = 0; } } #endif /* _BOOT */