/* * 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 2025 Oxide Computer Company * * 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); /* * The framebuffer address is not set; fall back to the serial console. */ if (fb_info.paddr == 0) return (CONS_TTY); #if defined(_BOOT) /* * If the firmware placed the framebuffer mapping above the 32-bit * boundary, we cannot use it from dboot. */ if (fb_info.paddr >= UINTPTR_MAX) return (CONS_TTY); #endif 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_chkparam_qmark(btem_state_t *btem, int c) { /* * This code is intentionally NOP, we do process * \E[?25h and \E[?25l, but our cursor is always shown. */ switch (c) { case 'h': /* DEC private mode set */ btem_setparam(btem, 1, 1); switch (btem->btem_params[0]) { case 25: /* show cursor */ break; } break; case 'l': /* DEC private mode reset */ btem_setparam(btem, 1, 1); switch (btem->btem_params[0]) { case 25: /* hide cursor */ break; } 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 { if (btem->btem_state == A_STATE_CSI_QMARK) btem_chkparam_qmark(btem, c); 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) { if (btem->btem_state != A_STATE_CSI) { btem_getparams(btem, c); return; } switch (c) { case '?': btem->btem_state = A_STATE_CSI_QMARK; return; default: 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 }; 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 */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2016 Toomas Soome */ #ifndef _BOOT_CONSOLE_IMPL_H #define _BOOT_CONSOLE_IMPL_H #include #include /* * Boot console implementation details. */ #ifdef __cplusplus extern "C" { #endif /* Console device callbacks. */ typedef struct bcons_dev { void (*bd_putchar)(int); void (*bd_eraseline)(void); void (*bd_cursor)(boolean_t); void (*bd_setpos)(int, int); void (*bd_shift)(int); } bcons_dev_t; extern boolean_t xbi_fb_init(struct xboot_info *, bcons_dev_t *); extern void boot_fb_init(int); extern void boot_vga_init(bcons_dev_t *); extern void boot_get_color(uint32_t *, uint32_t *); #ifdef __cplusplus } #endif #endif /* _BOOT_CONSOLE_IMPL_H */ /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2016 Toomas Soome */ /* * dboot and early kernel needs simple putchar(int) interface to implement * printf() support. So we implement simple interface on top of * linear frame buffer, since we can not use tem directly, we are * just borrowing bits from it. * * Note, this implementation is assuming UEFI linear frame buffer and * 32-bit depth, which should not be issue as GOP is supposed to provide those. * At the time of writing, this is the only case for frame buffer anyhow. */ #include #include #include #include #include #include #include #include #include "boot_console_impl.h" #define P2ROUNDUP(x, align) (-(-(x) & -(align))) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define nitems(x) (sizeof ((x)) / sizeof ((x)[0])) /* * Simplified visual_io data structures from visual_io.h */ struct vis_consdisplay { uint16_t row; /* Row to display data at */ uint16_t col; /* Col to display data at */ uint16_t width; /* Width of data */ uint16_t height; /* Height of data */ uint8_t *data; /* Data to display */ }; struct vis_conscopy { uint16_t s_row; /* Starting row */ uint16_t s_col; /* Starting col */ uint16_t e_row; /* Ending row */ uint16_t e_col; /* Ending col */ uint16_t t_row; /* Row to move to */ uint16_t t_col; /* Col to move to */ }; /* * We have largest font 16x32 with depth 32. This will allocate 2048 * bytes from BSS. */ #define MAX_GLYPH (16 * 32 * 4) struct fontlist cf_fontlist; static bitmap_data_t cf_data; static struct font cf_font; static struct font boot_fb_font; /* set by set_font() */ static uint8_t glyph[MAX_GLYPH]; static void boot_fb_putchar(int); static void boot_fb_eraseline(void); static void boot_fb_setpos(int, int); static void boot_fb_shiftline(int); static void boot_fb_eraseline_impl(uint16_t, uint16_t); static void xbi_init_font(struct xboot_info *xbi) { uint32_t i, checksum = 0; struct boot_modules *modules; struct font_info *fi; uintptr_t ptr; modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules; for (i = 0; i < xbi->bi_module_cnt; i++) { if (modules[i].bm_type == BMT_FONT) break; } if (i == xbi->bi_module_cnt) return; ptr = (uintptr_t)modules[i].bm_addr; fi = (struct font_info *)ptr; /* * Compute and verify checksum. The total sum of all the fields * must be 0. Note, the return from this point means we will * use default font. */ checksum += fi->fi_width; checksum += fi->fi_height; checksum += fi->fi_bitmap_size; for (i = 0; i < VFNT_MAPS; i++) checksum += fi->fi_map_count[i]; if (checksum + fi->fi_checksum != 0) return; cf_data.width = fi->fi_width; cf_data.height = fi->fi_height; cf_data.uncompressed_size = fi->fi_bitmap_size; cf_data.font = &cf_font; ptr += sizeof (struct font_info); ptr = P2ROUNDUP(ptr, 8); cf_font.vf_width = fi->fi_width; cf_font.vf_height = fi->fi_height; for (i = 0; i < VFNT_MAPS; i++) { if (fi->fi_map_count[i] == 0) continue; cf_font.vf_map_count[i] = fi->fi_map_count[i]; cf_font.vf_map[i] = (struct font_map *)ptr; ptr += (fi->fi_map_count[i] * sizeof (struct font_map)); ptr = P2ROUNDUP(ptr, 8); } cf_font.vf_bytes = (uint8_t *)ptr; cf_fontlist.font_name = NULL; cf_fontlist.font_flags = FONT_BOOT; cf_fontlist.font_data = &cf_data; cf_fontlist.font_load = NULL; STAILQ_INSERT_HEAD(&fonts, &cf_fontlist, font_next); } /* * extract data from MB2 framebuffer tag and set up initial frame buffer. */ boolean_t xbi_fb_init(struct xboot_info *xbi, bcons_dev_t *bcons_dev) { multiboot_tag_framebuffer_t *tag; boot_framebuffer_t *xbi_fb; xbi_fb = (boot_framebuffer_t *)(uintptr_t)xbi->bi_framebuffer; if (xbi_fb == NULL) return (B_FALSE); #if !defined(_BOOT) /* For early kernel, we get cursor position from dboot. */ fb_info.cursor.origin.x = xbi_fb->cursor.origin.x; fb_info.cursor.origin.y = xbi_fb->cursor.origin.y; fb_info.cursor.pos.x = xbi_fb->cursor.pos.x; fb_info.cursor.pos.y = xbi_fb->cursor.pos.y; fb_info.cursor.visible = xbi_fb->cursor.visible; #endif xbi_init_font(xbi); tag = (multiboot_tag_framebuffer_t *)(uintptr_t)xbi_fb->framebuffer; if (tag == NULL) { return (B_FALSE); } fb_info.paddr = tag->framebuffer_common.framebuffer_addr; fb_info.pitch = tag->framebuffer_common.framebuffer_pitch; fb_info.depth = tag->framebuffer_common.framebuffer_bpp; fb_info.bpp = P2ROUNDUP(fb_info.depth, 8) >> 3; fb_info.screen.x = tag->framebuffer_common.framebuffer_width; fb_info.screen.y = tag->framebuffer_common.framebuffer_height; fb_info.fb_size = fb_info.screen.y * fb_info.pitch; bcons_dev->bd_putchar = boot_fb_putchar; bcons_dev->bd_eraseline = boot_fb_eraseline; bcons_dev->bd_cursor = boot_fb_cursor; bcons_dev->bd_setpos = boot_fb_setpos; bcons_dev->bd_shift = boot_fb_shiftline; if (fb_info.paddr == 0) fb_info.fb_type = FB_TYPE_UNKNOWN; switch (tag->framebuffer_common.framebuffer_type) { case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT: fb_info.fb_type = FB_TYPE_EGA_TEXT; return (B_FALSE); case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED: if (fb_info.paddr != 0) fb_info.fb_type = FB_TYPE_INDEXED; return (B_TRUE); case MULTIBOOT_FRAMEBUFFER_TYPE_RGB: if (fb_info.paddr != 0) fb_info.fb_type = FB_TYPE_RGB; break; default: return (B_FALSE); } fb_info.rgb.red.size = tag->u.fb2.framebuffer_red_mask_size; fb_info.rgb.red.pos = tag->u.fb2.framebuffer_red_field_position; fb_info.rgb.green.size = tag->u.fb2.framebuffer_green_mask_size; fb_info.rgb.green.pos = tag->u.fb2.framebuffer_green_field_position; fb_info.rgb.blue.size = tag->u.fb2.framebuffer_blue_mask_size; fb_info.rgb.blue.pos = tag->u.fb2.framebuffer_blue_field_position; rgb_info = fb_info.rgb; return (B_TRUE); } /* set font and pass the data to fb_info */ static void boot_fb_set_font(uint16_t height, uint16_t width) { short h, w; bitmap_data_t *bp; int i; h = MIN(height, 4096); w = MIN(width, 4096); bp = set_font((short *)&fb_info.terminal.y, (short *)&fb_info.terminal.x, h, w); boot_fb_font.vf_bytes = bp->font->vf_bytes; boot_fb_font.vf_width = bp->font->vf_width; boot_fb_font.vf_height = bp->font->vf_height; for (i = 0; i < VFNT_MAPS; i++) { boot_fb_font.vf_map[i] = bp->font->vf_map[i]; boot_fb_font.vf_map_count[i] = bp->font->vf_map_count[i]; } fb_info.font_width = boot_fb_font.vf_width; fb_info.font_height = boot_fb_font.vf_height; } /* fill framebuffer */ static void boot_fb_fill(uint8_t *dst, uint32_t data, uint32_t len) { uint16_t *dst16; uint32_t *dst32; uint32_t i; switch (fb_info.depth) { case 24: case 8: for (i = 0; i < len; i++) dst[i] = (uint8_t)data; break; case 15: case 16: dst16 = (uint16_t *)dst; len /= 2; for (i = 0; i < len; i++) dst16[i] = (uint16_t)data; break; case 32: dst32 = (uint32_t *)dst; len /= 4; for (i = 0; i < len; i++) dst32[i] = data; break; } } /* copy data to framebuffer */ static void boot_fb_cpy(uint8_t *dst, uint8_t *src, uint32_t len) { uint16_t *dst16, *src16; uint32_t *dst32, *src32; switch (fb_info.depth) { case 24: case 8: default: if (dst <= src) { do { *dst++ = *src++; } while (--len != 0); } else { dst += len; src += len; do { *--dst = *--src; } while (--len != 0); } break; case 15: case 16: dst16 = (uint16_t *)dst; src16 = (uint16_t *)src; len /= 2; if (dst16 <= src16) { do { *dst16++ = *src16++; } while (--len != 0); } else { dst16 += len; src16 += len; do { *--dst16 = *--src16; } while (--len != 0); } break; case 32: dst32 = (uint32_t *)dst; src32 = (uint32_t *)src; len /= 4; if (dst32 <= src32) { do { *dst32++ = *src32++; } while (--len != 0); } else { dst32 += len; src32 += len; do { *--dst32 = *--src32; } while (--len != 0); } break; } } /* * Allocate shadow frame buffer, called from fakebop.c when early boot * allocator is ready. */ void boot_fb_shadow_init(bootops_t *bops) { if (boot_console_type(NULL) != CONS_FRAMEBUFFER) return; /* nothing to do */ fb_info.shadow_fb = (uint8_t *)bops->bsys_alloc(NULL, NULL, fb_info.fb_size, MMU_PAGESIZE); if (fb_info.shadow_fb == NULL) return; /* Copy FB to shadow */ boot_fb_cpy(fb_info.shadow_fb, fb_info.fb, fb_info.fb_size); } /* * Translate ansi color based on inverses and brightness. */ void boot_get_color(uint32_t *fg, uint32_t *bg) { /* ansi to solaris colors, see also boot_console.c */ if (fb_info.inverse == B_TRUE || fb_info.inverse_screen == B_TRUE) { if (fb_info.fg_color < XLATE_NCOLORS) { /* * white fg -> bright white bg */ if (fb_info.fg_color == pc_white) *bg = brt_xlate[fb_info.fg_color]; else *bg = dim_xlate[fb_info.fg_color]; } else { *bg = fb_info.fg_color; } if (fb_info.bg_color < XLATE_NCOLORS) { if (fb_info.bg_color == pc_white) *fg = brt_xlate[fb_info.bg_color]; else *fg = dim_xlate[fb_info.bg_color]; } else { *fg = fb_info.bg_color; } } else { if (fb_info.fg_color < XLATE_NCOLORS) { if (fb_info.fg_color == pc_white) *fg = brt_xlate[fb_info.fg_color]; else *fg = dim_xlate[fb_info.fg_color]; } else { *fg = fb_info.fg_color; } if (fb_info.bg_color < XLATE_NCOLORS) { if (fb_info.bg_color == pc_white) *bg = brt_xlate[fb_info.bg_color]; else *bg = dim_xlate[fb_info.bg_color]; } else { *bg = fb_info.bg_color; } } } /* * Map indexed color to RGB value. */ uint32_t boot_color_map(uint8_t index) { if (fb_info.fb_type != FB_TYPE_RGB) { if (index < nitems(solaris_color_to_pc_color)) return (solaris_color_to_pc_color[index]); else return (index); } return (rgb_color_map(&fb_info.rgb, index, 0)); } /* set up out simple console. */ /*ARGSUSED*/ void boot_fb_init(int console) { fb_info_pixel_coord_t window; /* frame buffer address is mapped in dboot. */ fb_info.fb = (uint8_t *)(uintptr_t)fb_info.paddr; boot_fb_set_font(fb_info.screen.y, fb_info.screen.x); window.x = (fb_info.screen.x - fb_info.terminal.x * boot_fb_font.vf_width) / 2; window.y = (fb_info.screen.y - fb_info.terminal.y * boot_fb_font.vf_height) / 2; fb_info.terminal_origin.x = window.x; fb_info.terminal_origin.y = window.y; #if defined(_BOOT) /* * Being called from dboot, we can have cursor terminal * position passed from boot loader. In such case, fix the * cursor screen coords. */ if (fb_info.cursor.pos.x != 0 || fb_info.cursor.pos.y != 0) { fb_info.cursor.origin.x = window.x + fb_info.cursor.pos.x * boot_fb_font.vf_width; fb_info.cursor.origin.y = window.y + fb_info.cursor.pos.y * boot_fb_font.vf_height; } #endif /* If the cursor terminal position is 0,0 just reset screen coords */ if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0) { fb_info.cursor.origin.x = window.x; fb_info.cursor.origin.y = window.y; } /* * Validate cursor coords with screen/terminal dimensions, * if anything is off, reset to 0,0 */ if (fb_info.cursor.pos.x > fb_info.terminal.x || fb_info.cursor.pos.y > fb_info.terminal.y || fb_info.cursor.origin.x > fb_info.screen.x || fb_info.cursor.origin.y > fb_info.screen.y) { fb_info.cursor.origin.x = window.x; fb_info.cursor.origin.y = window.y; fb_info.cursor.pos.x = 0; fb_info.cursor.pos.y = 0; } #if defined(_BOOT) /* clear the screen if cursor is set to 0,0 */ if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0) { uint32_t fg, bg, toffset; uint16_t y; boot_get_color(&fg, &bg); bg = boot_color_map(bg); toffset = 0; for (y = 0; y < fb_info.screen.y; y++) { uint8_t *dest = fb_info.fb + toffset; boot_fb_fill(dest, bg, fb_info.pitch); toffset += fb_info.pitch; } } #endif } /* copy rectangle to framebuffer. */ static void boot_fb_blit(struct vis_consdisplay *rect) { uint32_t offset, size; /* write size per scanline */ uint8_t *fbp, *sfbp = NULL; /* fb + calculated offset */ int i; /* make sure we will not write past FB */ if (rect->col >= fb_info.screen.x || rect->row >= fb_info.screen.y || rect->col + rect->width >= fb_info.screen.x || rect->row + rect->height >= fb_info.screen.y) return; size = rect->width * fb_info.bpp; offset = rect->col * fb_info.bpp + rect->row * fb_info.pitch; fbp = fb_info.fb + offset; if (fb_info.shadow_fb != NULL) sfbp = fb_info.shadow_fb + offset; /* write all scanlines in rectangle */ for (i = 0; i < rect->height; i++) { uint8_t *dest = fbp + i * fb_info.pitch; uint8_t *src = rect->data + i * size; boot_fb_cpy(dest, src, size); if (sfbp != NULL) { dest = sfbp + i * fb_info.pitch; boot_fb_cpy(dest, src, size); } } } static void bit_to_pix(uchar_t c) { uint32_t fg, bg; boot_get_color(&fg, &bg); fg = boot_color_map(fg); bg = boot_color_map(bg); switch (fb_info.depth) { case 8: font_bit_to_pix8(&boot_fb_font, (uint8_t *)glyph, c, fg, bg); break; case 15: case 16: font_bit_to_pix16(&boot_fb_font, (uint16_t *)glyph, c, (uint16_t)fg, (uint16_t)bg); break; case 24: font_bit_to_pix24(&boot_fb_font, (uint8_t *)glyph, c, fg, bg); break; case 32: font_bit_to_pix32(&boot_fb_font, (uint32_t *)glyph, c, fg, bg); break; } } static void boot_fb_eraseline_impl(uint16_t x, uint16_t y) { uint32_t toffset, size; uint32_t fg, bg; uint8_t *dst, *sdst; int i; boot_get_color(&fg, &bg); bg = boot_color_map(bg); size = fb_info.terminal.x * boot_fb_font.vf_width * fb_info.bpp; toffset = x * fb_info.bpp + y * fb_info.pitch; dst = fb_info.fb + toffset; sdst = fb_info.shadow_fb + toffset; for (i = 0; i < boot_fb_font.vf_height; i++) { uint8_t *dest = dst + i * fb_info.pitch; if (fb_info.fb + fb_info.fb_size >= dest + size) boot_fb_fill(dest, bg, size); if (fb_info.shadow_fb != NULL) { dest = sdst + i * fb_info.pitch; if (fb_info.shadow_fb + fb_info.fb_size >= dest + size) { boot_fb_fill(dest, bg, size); } } } } static void boot_fb_eraseline(void) { boot_fb_eraseline_impl(fb_info.cursor.origin.x, fb_info.cursor.origin.y); } /* * Copy rectangle from console to console. * If shadow buffer is available, use shadow as source. */ static void boot_fb_conscopy(struct vis_conscopy *c_copy) { uint32_t soffset, toffset; uint32_t width, height, increment; uint8_t *src, *dst, *sdst = NULL; int i; soffset = c_copy->s_col * fb_info.bpp + c_copy->s_row * fb_info.pitch; toffset = c_copy->t_col * fb_info.bpp + c_copy->t_row * fb_info.pitch; src = fb_info.fb + soffset; dst = fb_info.fb + toffset; if (fb_info.shadow_fb != NULL) { src = fb_info.shadow_fb + soffset; sdst = fb_info.shadow_fb + toffset; } width = (c_copy->e_col - c_copy->s_col + 1) * fb_info.bpp; height = c_copy->e_row - c_copy->s_row + 1; for (i = 0; i < height; i++) { increment = i * fb_info.pitch; /* Make sure we fit into FB size. */ if (soffset + increment + width >= fb_info.fb_size || toffset + increment + width >= fb_info.fb_size) break; boot_fb_cpy(dst + increment, src + increment, width); if (sdst != NULL) boot_fb_cpy(sdst + increment, src + increment, width); } } /* Shift the line content by chars. */ static void boot_fb_shiftline(int chars) { struct vis_conscopy c_copy; c_copy.s_col = fb_info.cursor.origin.x; c_copy.s_row = fb_info.cursor.origin.y; c_copy.e_col = (fb_info.terminal.x - chars) * boot_fb_font.vf_width; c_copy.e_col += fb_info.terminal_origin.x; c_copy.e_row = c_copy.s_row + boot_fb_font.vf_height; c_copy.t_col = fb_info.cursor.origin.x + chars * boot_fb_font.vf_width; c_copy.t_row = fb_info.cursor.origin.y; boot_fb_conscopy(&c_copy); } /* * move the terminal window lines [1..y] to [0..y-1] and clear last line. */ static void boot_fb_scroll(void) { struct vis_conscopy c_copy; /* support for scrolling. set up the console copy data and last line */ c_copy.s_row = fb_info.terminal_origin.y + boot_fb_font.vf_height; c_copy.s_col = fb_info.terminal_origin.x; c_copy.e_row = fb_info.screen.y - fb_info.terminal_origin.y; c_copy.e_col = fb_info.screen.x - fb_info.terminal_origin.x; c_copy.t_row = fb_info.terminal_origin.y; c_copy.t_col = fb_info.terminal_origin.x; boot_fb_conscopy(&c_copy); /* now clean up the last line */ boot_fb_eraseline_impl(fb_info.terminal_origin.x, fb_info.terminal_origin.y + (fb_info.terminal.y - 1) * boot_fb_font.vf_height); } /* * Very simple block cursor. Save space below the cursor and restore * when cursor is invisible. */ void boot_fb_cursor(boolean_t visible) { uint32_t offset, size, j; uint32_t *fb32, *sfb32 = NULL; uint32_t fg, bg; uint16_t *fb16, *sfb16 = NULL; uint8_t *fb8, *sfb8 = NULL; int i, pitch; if (fb_info.cursor.visible == visible) return; boot_get_color(&fg, &bg); fg = boot_color_map(fg); bg = boot_color_map(bg); fb_info.cursor.visible = visible; pitch = fb_info.pitch; size = boot_fb_font.vf_width * fb_info.bpp; /* * Build cursor image. We are building mirror image of data on * frame buffer by (D xor FG) xor BG. */ offset = fb_info.cursor.origin.x * fb_info.bpp + fb_info.cursor.origin.y * pitch; switch (fb_info.depth) { case 8: for (i = 0; i < boot_fb_font.vf_height; i++) { fb8 = fb_info.fb + offset + i * pitch; if (fb_info.shadow_fb != NULL) sfb8 = fb_info.shadow_fb + offset + i * pitch; for (j = 0; j < size; j += 1) { fb8[j] = (fb8[j] ^ (fg & 0xff)) ^ (bg & 0xff); if (sfb8 == NULL) continue; sfb8[j] = (sfb8[j] ^ (fg & 0xff)) ^ (bg & 0xff); } } break; case 15: case 16: for (i = 0; i < boot_fb_font.vf_height; i++) { fb16 = (uint16_t *)(fb_info.fb + offset + i * pitch); if (fb_info.shadow_fb != NULL) sfb16 = (uint16_t *) (fb_info.shadow_fb + offset + i * pitch); for (j = 0; j < boot_fb_font.vf_width; j++) { fb16[j] = (fb16[j] ^ (fg & 0xffff)) ^ (bg & 0xffff); if (sfb16 == NULL) continue; sfb16[j] = (sfb16[j] ^ (fg & 0xffff)) ^ (bg & 0xffff); } } break; case 24: for (i = 0; i < boot_fb_font.vf_height; i++) { fb8 = fb_info.fb + offset + i * pitch; if (fb_info.shadow_fb != NULL) sfb8 = fb_info.shadow_fb + offset + i * pitch; for (j = 0; j < size; j += 3) { fb8[j] = (fb8[j] ^ ((fg >> 16) & 0xff)) ^ ((bg >> 16) & 0xff); fb8[j+1] = (fb8[j+1] ^ ((fg >> 8) & 0xff)) ^ ((bg >> 8) & 0xff); fb8[j+2] = (fb8[j+2] ^ (fg & 0xff)) ^ (bg & 0xff); if (sfb8 == NULL) continue; sfb8[j] = (sfb8[j] ^ ((fg >> 16) & 0xff)) ^ ((bg >> 16) & 0xff); sfb8[j+1] = (sfb8[j+1] ^ ((fg >> 8) & 0xff)) ^ ((bg >> 8) & 0xff); sfb8[j+2] = (sfb8[j+2] ^ (fg & 0xff)) ^ (bg & 0xff); } } break; case 32: for (i = 0; i < boot_fb_font.vf_height; i++) { fb32 = (uint32_t *)(fb_info.fb + offset + i * pitch); if (fb_info.shadow_fb != NULL) { sfb32 = (uint32_t *) (fb_info.shadow_fb + offset + i * pitch); } for (j = 0; j < boot_fb_font.vf_width; j++) { fb32[j] = (fb32[j] ^ fg) ^ bg; if (sfb32 == NULL) continue; sfb32[j] = (sfb32[j] ^ fg) ^ bg; } } break; } } static void boot_fb_setpos(int row, int col) { if (row < 0) row = 0; if (row >= fb_info.terminal.y) row = fb_info.terminal.y - 1; if (col < 0) col = 0; if (col >= fb_info.terminal.x) col = fb_info.terminal.x - 1; fb_info.cursor.pos.x = col; fb_info.cursor.pos.y = row; fb_info.cursor.origin.x = fb_info.terminal_origin.x; fb_info.cursor.origin.x += col * boot_fb_font.vf_width; fb_info.cursor.origin.y = fb_info.terminal_origin.y; fb_info.cursor.origin.y += row * boot_fb_font.vf_height; } static void boot_fb_putchar(int c) { struct vis_consdisplay display; int rows, cols; rows = fb_info.cursor.pos.y; cols = fb_info.cursor.pos.x; if (c == '\n') { if (rows < fb_info.terminal.y - 1) boot_fb_setpos(rows + 1, cols); else boot_fb_scroll(); return; } bit_to_pix(c); display.col = fb_info.cursor.origin.x; display.row = fb_info.cursor.origin.y; display.width = boot_fb_font.vf_width; display.height = boot_fb_font.vf_height; display.data = glyph; boot_fb_blit(&display); if (cols < fb_info.terminal.x - 1) boot_fb_setpos(rows, cols + 1); else if (rows < fb_info.terminal.y - 1) boot_fb_setpos(rows + 1, 0); else { boot_fb_setpos(rows, 0); boot_fb_scroll(); } } /* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2020 Joyent, Inc. * Copyright 2022 Oxide Computer Company */ /* * The boot GDT must remain in sync with the entries in intel/sys/segments.h; in * particular kmdb uses B64CODE_SEL or B32CODE_SEL in perpetuity for its IDT * entries (they're copied to the kernel's GDT in init_idt()). * * The GDT is effectively an array of user_desc_t entries. */ .align 16 .data global_descriptor_table: .long 0 .long 0 /* GDT_B32DATA: 32 bit flat data descriptor */ .value 0xFFFF /* segment limit 0..15 */ .value 0x0000 /* segment base 0..15 */ .byte 0x0 /* segment base 16..23 */ .byte 0x92 /* P = 1, read/write data */ .byte 0xCF /* G=1, B=1, Limit (16..19)=1111 */ .byte 0x0 /* segment base 24..32 */ /* GDT_B32CODE 32 bit flat code descriptor */ .value 0xFFFF /* segment limit 0..15 */ .value 0x0000 /* segment base 0..15 */ .byte 0x0 /* segment base 16..23 */ .byte 0x9A /* P=1, code, exec, readable */ .byte 0xCF /* G=1, D=1, Limit (16..19)=1111 */ .byte 0x0 /* segment base 24..32 */ /* * GDT_B16CODE 16 bit code descriptor for doing BIOS calls */ .value 0xFFFF /* segment limit 0..15 */ .value 0x0000 /* segment base 0..15 */ .byte 0x0 /* segment base 16..23 */ .byte 0x9A /* P=1, code, exec, readable */ .byte 0x0F /* G=0, D=0, Limit (16..19)=1111 */ .byte 0x0 /* segment base 24..32 */ /* * GDT_B16DATA 16 bit data descriptor for doing BIOS calls * XXX: Note that this sets the B flag, which is not supposed to be * set for a 16-bit data segment that is not expand-down (Intel SDM * vol 3A sec 3.4.5). AMD does not seem to care (AMD APM vol 2 sec * 2.7.4). It is likely this is here for use with stack segments, * where it effects 32-bit operations. Conceivably, we could be more * conservative and specify a 16-bit stack segment, but leave it for * now. */ .value 0xFFFF /* segment limit 0..15 */ .value 0x0000 /* segment base 0..15 */ .byte 0x0 /* segment base 16..23 */ .byte 0x92 /* P = 1, read/write data */ .byte 0x4F /* G=0, B=1, Limit (16..19)=1111 */ .byte 0x0 /* segment base 24..32 */ /* GDT_B64CODE: 64 bit flat code descriptor - only L bit has meaning */ .value 0xFFFF /* segment limit 0..15 */ .value 0x0000 /* segment base 0..15 */ .byte 0x0 /* segment base 16..23 */ .byte 0x9A /* P=1, code, exec, readable */ .byte 0xAF /* G=1, D=0, L=1, Limit (16..19)=1111 */ .byte 0x0 /* segment base 24..32 */ /* * unused */ .long 0 .long 0 /* * GDT_BGSTMP -- an entry for kmdb to use during boot * the fast reboot code uses this entry for memory copies, too. */ .value 0x0001 /* segment limit 0..15 */ .globl fake_cpu_gdt_base_0_15 fake_cpu_gdt_base_0_15: .value 0x0000 /* segment base 0..15 */ .globl fake_cpu_gdt_base_16_23 fake_cpu_gdt_base_16_23: .byte 0x0 /* segment base 16..23 */ .byte 0x9A /* P=1, code, exec, readable */ .byte 0xC0 /* G=1, D=1, Limit (16..19)=0000 */ .globl fake_cpu_gdt_base_24_31 fake_cpu_gdt_base_24_31: .byte 0x0 /* segment base 24..32 */ / .long 0 / .long 0 /* * This is a desctbr_t. */ gdt_info: .value gdt_info - global_descriptor_table - 1 .long global_descriptor_table .long 0 /* needed for 64 bit */ fake_cpu: .4byte 0 .4byte 0 .4byte 0 .globl fake_cpu_ptr fake_cpu_ptr: .4byte 0 .skip 0x6c0, 0 /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Miniature keyboard driver for bootstrap. This allows keyboard * support to continue after we take over interrupts and disable * BIOS keyboard support. */ #include #include #include #include "boot_keyboard_table.h" #if defined(_BOOT) #include "dboot/dboot_asm.h" #include "dboot/dboot_xboot.h" #endif /* _BOOT */ /* * Definitions for BIOS keyboard state. We use BIOS's variable to store * state, ensuring that we stay in sync with it. */ #define BIOS_KB_FLAG 0x417 #define BIOS_RIGHT_SHIFT 0x01 #define BIOS_LEFT_SHIFT 0x02 #define BIOS_EITHER_SHIFT (BIOS_LEFT_SHIFT | BIOS_RIGHT_SHIFT) #define BIOS_CTL_SHIFT 0x04 #define BIOS_ALT_SHIFT 0x08 #define BIOS_SCROLL_STATE 0x10 #define BIOS_NUM_STATE 0x20 #define BIOS_CAPS_STATE 0x40 #define BIOS_INS_STATE 0x80 #define BIOS_KB_FLAG_1 0x418 #define BIOS_SYS_SHIFT 0x04 #define BIOS_HOLD_STATE 0x08 #define BIOS_SCROLL_SHIFT 0x10 #define BIOS_NUM_SHIFT 0x20 #define BIOS_CAPS_SHIFT 0x40 #define BIOS_INS_SHIFT 0x80 #if defined(__xpv) && defined(_BOOT) /* * Device memory addresses * * In dboot under the hypervisor we don't have any memory mappings * for the first meg of low memory so we can't access devices there. * Intead we've mapped the device memory that we need to access into * a local variable within dboot so we can access the device memory * there. */ extern unsigned short *kb_status; #define kb_flag ((unsigned char *)&kb_status[BIOS_KB_FLAG]) #define kb_flag_1 ((unsigned char *)&kb_status[BIOS_KB_FLAG_1]) #else /* __xpv && _BOOT */ /* Device memory addresses */ static unsigned char *kb_status = ((unsigned char *)BIOS_KB_FLAG); #define kb_flag (&kb_status[0]) #define kb_flag_1 (&kb_status[1]) #endif /* __xpv && _BOOT */ /* * Keyboard controller registers */ #define I8042_DATA 0x60 #define I8042_STAT 0x64 #define I8042_CMD 0x64 /* * Keyboard controller status register bits */ #define I8042_STAT_OUTBF 0x01 #define I8042_STAT_INBF 0x02 #define I8042_STAT_AUXBF 0x20 /* * Keyboard controller commands */ #define I8042_RCB 0x20 #define I8042_WCB 0x60 /* * Keyboard commands */ #define KB_SET_LED 0xED /* LED byte follows... */ #define KB_LED_SCROLL_LOCK 0x01 /* Bits for LED byte */ #define KB_LED_NUM_LOCK 0x02 #define KB_LED_CAPS_LOCK 0x04 #ifndef ASSERT #define ASSERT(x) #endif #define peek8(p) (*(p)) #define poke8(p, val) (*(p) = (val)) static struct { boolean_t initialized; enum { KB_LED_IDLE, KB_LED_COMMAND_SENT, KB_LED_VALUE_SENT } led_state; int led_commanded; /* * Possible values: * * -1 Nothing pending * 0x000-0x0ff Pending byte * 0x100-0x1ff Needs leading zero, then low byte next. * * Others undefined. */ int pending; } kb = { B_FALSE, /* initialized? */ KB_LED_IDLE, /* LED command state */ -1, /* commanded LEDs - force refresh */ -1, /* pending */ }; #define KTAB_STRLEN 3 static char keystringtab[KTAB_STRLEN] = {'\033', '[', ' '}; static int keystring = -1; static int kb_translate(unsigned char code); static void kb_send(unsigned char cmd); static void kb_update_leds(void); static uchar_t kb_calculate_leds(void); int kb_getchar(void) { int ret; while (!kb_ischar()) /* LOOP */; if (keystring >= 0) { ret = keystringtab[keystring++]; if (keystring == KTAB_STRLEN) { keystring = -1; kb.pending = -1; } return (ret); } /* * kb_ischar() doesn't succeed without leaving kb.pending * set. */ ASSERT(kb.pending >= 0); if (kb.pending & 0x100) { kb.pending &= 0xff; switch (kb.pending) { case 'H': /* Up */ keystringtab[2] = 'A'; keystring = 0; return (kb_getchar()); case 'P': /* Down */ keystringtab[2] = 'B'; keystring = 0; return (kb_getchar()); case 'M': /* Right */ keystringtab[2] = 'C'; keystring = 0; return (kb_getchar()); case 'K': /* Left */ keystringtab[2] = 'D'; keystring = 0; return (kb_getchar()); default: ret = 0; } } else { ret = kb.pending; kb.pending = -1; } return (ret); } int kb_ischar(void) { unsigned char buffer_stat; unsigned char code; unsigned char leds; if (!kb.initialized) { kb_init(); kb.initialized = B_TRUE; } if (kb.pending >= 0) return (1); for (;;) { buffer_stat = inb(I8042_STAT); if (buffer_stat == 0xff) return (0); buffer_stat &= (I8042_STAT_OUTBF | I8042_STAT_AUXBF); switch (buffer_stat) { case 0: case I8042_STAT_AUXBF: return (0); case (I8042_STAT_OUTBF | I8042_STAT_AUXBF): /* * Discard unwanted mouse data. */ (void) inb(I8042_DATA); continue; } code = inb(I8042_DATA); switch (code) { /* * case 0xAA: * * You might think that we should ignore 0xAA on the * grounds that it is the BAT Complete response and will * occur on keyboard detach/reattach. Unfortunately, * it is ambiguous - this is also the code for a break * of the left shift key. Since it will be harmless for * us to "spuriously" process a break of Left Shift, * we just let the normal code handle it. Perhaps we * should take a hint and refresh the LEDs, but I * refuse to get very worried about hot-plug issues * in this mini-driver. */ case 0xFA: switch (kb.led_state) { case KB_LED_IDLE: /* * Spurious. Oh well, ignore it. */ break; case KB_LED_COMMAND_SENT: leds = kb_calculate_leds(); kb_send(leds); kb.led_commanded = leds; kb.led_state = KB_LED_VALUE_SENT; break; case KB_LED_VALUE_SENT: kb.led_state = KB_LED_IDLE; /* * Check for changes made while we were * working on the last change. */ kb_update_leds(); break; } continue; case 0xE0: case 0xE1: /* * These are used to distinguish the keys added on * the AT-101 keyboard from the original 84 keys. * We don't care, and the codes are carefully arranged * so that we don't have to. */ continue; default: if (code & 0x80) { /* Release */ code &= 0x7f; switch (keyboard_translate[code].normal) { case KBTYPE_SPEC_LSHIFT: poke8(kb_flag, peek8(kb_flag) & ~BIOS_LEFT_SHIFT); break; case KBTYPE_SPEC_RSHIFT: poke8(kb_flag, peek8(kb_flag) & ~BIOS_RIGHT_SHIFT); break; case KBTYPE_SPEC_CTRL: poke8(kb_flag, peek8(kb_flag) & ~BIOS_CTL_SHIFT); break; case KBTYPE_SPEC_ALT: poke8(kb_flag, peek8(kb_flag) & ~BIOS_ALT_SHIFT); break; case KBTYPE_SPEC_CAPS_LOCK: poke8(kb_flag_1, peek8(kb_flag_1) & ~BIOS_CAPS_SHIFT); break; case KBTYPE_SPEC_NUM_LOCK: poke8(kb_flag_1, peek8(kb_flag_1) & ~BIOS_NUM_SHIFT); break; case KBTYPE_SPEC_SCROLL_LOCK: poke8(kb_flag_1, peek8(kb_flag_1) & ~BIOS_SCROLL_SHIFT); break; default: /* * Ignore all other releases. */ break; } } else { /* Press */ kb.pending = kb_translate(code); if (kb.pending >= 0) { return (1); } } } } } int kb_translate(unsigned char code) { struct keyboard_translate *k; unsigned short action; boolean_t shifted; k = keyboard_translate + code; shifted = (peek8(kb_flag) & BIOS_EITHER_SHIFT) != 0; switch (k->normal & 0xFF00) { case KBTYPE_NUMPAD: if (peek8(kb_flag) & BIOS_NUM_STATE) shifted = !shifted; break; case KBTYPE_ALPHA: if (peek8(kb_flag) & BIOS_CAPS_STATE) shifted = !shifted; break; } if (peek8(kb_flag) & BIOS_ALT_SHIFT) action = k->alted; else if (peek8(kb_flag) & BIOS_CTL_SHIFT) action = k->ctrled; else if (shifted) action = k->shifted; else action = k->normal; switch (action & 0xFF00) { case KBTYPE_NORMAL: case KBTYPE_ALPHA: return (action & 0xFF); case KBTYPE_NUMPAD: case KBTYPE_FUNC: return ((action & 0xFF) | 0x100); case KBTYPE_SPEC: break; default: /* * Bad entry. */ ASSERT(0); return (-1); } /* * Handle special keys, mostly shifts. */ switch (action) { case KBTYPE_SPEC_NOP: case KBTYPE_SPEC_UNDEF: break; case KBTYPE_SPEC_LSHIFT: poke8(kb_flag, peek8(kb_flag) | BIOS_LEFT_SHIFT); break; case KBTYPE_SPEC_RSHIFT: poke8(kb_flag, peek8(kb_flag) | BIOS_RIGHT_SHIFT); break; case KBTYPE_SPEC_CTRL: poke8(kb_flag, peek8(kb_flag) | BIOS_CTL_SHIFT); break; case KBTYPE_SPEC_ALT: poke8(kb_flag, peek8(kb_flag) | BIOS_ALT_SHIFT); break; case KBTYPE_SPEC_CAPS_LOCK: if (!(peek8(kb_flag_1) & BIOS_CAPS_SHIFT)) { poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_CAPS_SHIFT); poke8(kb_flag, peek8(kb_flag) ^ BIOS_CAPS_STATE); } break; case KBTYPE_SPEC_NUM_LOCK: if (!(peek8(kb_flag_1) & BIOS_NUM_SHIFT)) { poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_NUM_SHIFT); poke8(kb_flag, peek8(kb_flag) ^ BIOS_NUM_STATE); } break; case KBTYPE_SPEC_SCROLL_LOCK: if (!(peek8(kb_flag_1) & BIOS_SCROLL_SHIFT)) { poke8(kb_flag_1, peek8(kb_flag_1) | BIOS_SCROLL_SHIFT); poke8(kb_flag, peek8(kb_flag) ^ BIOS_SCROLL_STATE); } break; case KBTYPE_SPEC_MAYBE_REBOOT: #if 0 /* Solaris doesn't reboot via ctrl-alt-del */ if ((peek8(kb_flag) & (BIOS_CTL_SHIFT|BIOS_ALT_SHIFT)) == (BIOS_CTL_SHIFT|BIOS_ALT_SHIFT)) { reset(); /* NOTREACHED */ } #endif break; default: /* * Bad entry */ ASSERT(0); break; } /* * Consider updating the LEDs. This does nothing if nothing * needs to be done. */ kb_update_leds(); return (-1); } void kb_send(unsigned char cmd) { int retries; for (retries = 0; (inb(I8042_STAT) & I8042_STAT_INBF) != 0 && retries < 100000; retries++) /* LOOP */; outb(I8042_DATA, cmd); } void kb_update_leds(void) { if (kb.led_state != KB_LED_IDLE) { /* * The state machine will take care of any additional * changes that are necessary. */ return; } if (kb_calculate_leds() == kb.led_commanded) { kb.led_state = KB_LED_IDLE; } else { kb_send(KB_SET_LED); kb.led_state = KB_LED_COMMAND_SENT; } } #define MIMR_PORT 0x21 /* Mask register for master PIC */ #define MIMR_KB 2 /* Keyboard mask bit in master PIC */ void kb_init(void) { /* * Resist the urge to muck with the keyboard/mouse. Just assume * that the bios, grub, and any optional hypervisor have left * the keyboard in a sane and usable state. Messing with it now * could result it making it unusuable, which would break early * kmdb debugging support. Note that we don't actually need to * disable interrupts for the keyboard/mouse since we're already * in protected mode and we're not compeating with the bios for * keyboard access. Also, we don't need to disable the mouse * port since our polled input routine will just drop any mouse * data that it recieves. */ kb_update_leds(); } unsigned char kb_calculate_leds(void) { int res; res = 0; if (peek8(kb_flag) & BIOS_CAPS_STATE) res |= KB_LED_CAPS_LOCK; if (peek8(kb_flag) & BIOS_NUM_STATE) res |= KB_LED_NUM_LOCK; if (peek8(kb_flag) & BIOS_SCROLL_STATE) res |= KB_LED_SCROLL_LOCK; return ((char)res); } /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Keyboard table for bootstrap's simple keyboard driver. */ #include "boot_keyboard_table.h" #define A | KBTYPE_ALPHA #define C & 0x1f #define F | KBTYPE_FUNC #define N | KBTYPE_NUMPAD #define ALT KBTYPE_SPEC_ALT #define CTRL KBTYPE_SPEC_CTRL #define LSHIFT KBTYPE_SPEC_LSHIFT #define NOP KBTYPE_SPEC_NOP #define NUMLK KBTYPE_SPEC_NUM_LOCK #define SCRLLK KBTYPE_SPEC_SCROLL_LOCK #define CAPSLK KBTYPE_SPEC_CAPS_LOCK #define RSHIFT KBTYPE_SPEC_RSHIFT #define REBOOT KBTYPE_SPEC_MAYBE_REBOOT #define UNDEF KBTYPE_SPEC_UNDEF struct keyboard_translate keyboard_translate[128] = { /* Normal Shifted Ctrled Alted */ /* 00 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 01 */ '['C, '['C, NOP, NOP, /* 02 */ '1', '!', NOP, 0x78 F, /* 03 */ '2', '@', NOP, 0x79 F, /* 04 */ '3', '#', NOP, 0x7a F, /* 05 */ '4', '$', NOP, 0x7b F, /* 06 */ '5', '%', NOP, 0x7c F, /* 07 */ '6', '^', '^'C, 0x7d F, /* 08 */ '7', '&', NOP, 0x7e F, /* 09 */ '8', '*', NOP, 0x7f F, /* 0a */ '9', '(', NOP, 0x80 F, /* 0b */ '0', ')', NOP, 0x81 F, /* 0c */ '-', '_', NOP, 0x82 F, /* 0d */ '=', '+', NOP, 0x83 F, /* 0e */ 'h'C, 0x0e F, 0x7f, NOP, /* 0f */ 'i'C, 0x0f F, NOP, NOP, /* 10 */ 'q'A, 'Q', 'q'C, 0x10 F, /* 11 */ 'w'A, 'W', 'w'C, 0x11 F, /* 12 */ 'e'A, 'E', 'e'C, 0x12 F, /* 13 */ 'r'A, 'R', 'r'C, 0x13 F, /* 14 */ 't'A, 'T', 't'C, 0x14 F, /* 15 */ 'y'A, 'Y', 'y'C, 0x15 F, /* 16 */ 'u'A, 'U', 'u'C, 0x16 F, /* 17 */ 'i'A, 'I', 'i'C, 0x17 F, /* 18 */ 'o'A, 'O', 'o'C, 0x18 F, /* 19 */ 'p'A, 'P', 'p'C, 0x19 F, /* 1a */ '[', '{', '['C, NOP, /* 1b */ ']', '}', ']'C, NOP, /* 1c */ 'm'C, 'm'C, NOP, NOP, /* 1d */ CTRL, CTRL, CTRL, CTRL, /* 1e */ 'a'A, 'A', 'a'C, 0x1e F, /* 1f */ 's'A, 'S', 's'C, 0x1f F, /* 20 */ 'd'A, 'D', 'd'C, 0x20 F, /* 21 */ 'f'A, 'F', 'f'C, 0x21 F, /* 22 */ 'g'A, 'G', 'g'C, 0x22 F, /* 23 */ 'h'A, 'H', 'h'C, 0x23 F, /* 24 */ 'j'A, 'J', 'j'C, 0x24 F, /* 25 */ 'k'A, 'K', 'k'C, 0x25 F, /* 26 */ 'l'A, 'L', 'l'C, 0x26 F, /* 27 */ ';', ':', NOP, NOP, /* 28 */ '\'', '"', NOP, NOP, /* 29 */ '`', '~', NOP, NOP, /* 2a */ LSHIFT, LSHIFT, LSHIFT, LSHIFT, /* 2b */ '\\', '|', '\\'C, NOP, /* 2c */ 'z'A, 'Z', 'z'C, 0x2c F, /* 2d */ 'x'A, 'X', 'x'C, 0x2d F, /* 2e */ 'c'A, 'C', 'c'C, 0x2e F, /* 2f */ 'v'A, 'V', 'v'C, 0x2f F, /* 30 */ 'b'A, 'B', 'b'C, 0x30 F, /* 31 */ 'n'A, 'N', 'n'C, 0x31 F, /* 32 */ 'm'A, 'M', 'm'C, 0x32 F, /* 33 */ ',', '<', NOP, NOP, /* 34 */ '.', '>', NOP, NOP, /* 35 */ '/', '?', NOP, NOP, /* 36 */ RSHIFT, RSHIFT, RSHIFT, RSHIFT, /* 37 */ '*', NOP, NOP, NOP, /* * PrtSc */ /* 38 */ ALT, ALT, ALT, ALT, /* 39 */ ' ', ' ', NOP, NOP, /* 3a */ CAPSLK, CAPSLK, CAPSLK, CAPSLK, /* 3b */ 0x3b F, 0x54 F, 0x5e F, 0x68 F, /* 3c */ 0x3c F, 0x55 F, 0x5f F, 0x69 F, /* 3d */ 0x3d F, 0x56 F, 0x60 F, 0x6a F, /* 3e */ 0x3e F, 0x57 F, 0x61 F, 0x6b F, /* 3f */ 0x3f F, 0x58 F, 0x62 F, 0x6c F, /* 40 */ 0x40 F, 0x59 F, 0x63 F, 0x6d F, /* 41 */ 0x41 F, 0x5a F, 0x64 F, 0x6e F, /* 42 */ 0x42 F, 0x5b F, 0x65 F, 0x6f F, /* 43 */ 0x43 F, 0x5c F, 0x66 F, 0x70 F, /* 44 */ 0x44 F, 0x5d F, 0x67 F, 0x71 F, /* 45 */ NUMLK, NUMLK, NUMLK, NUMLK, /* 46 */ SCRLLK, SCRLLK, SCRLLK, SCRLLK, /* 47 */ 0x47 N, '7', NOP, NOP, /* 48 */ 0x48 N, '8', NOP, NOP, /* 49 */ 0x49 N, '9', NOP, NOP, /* 4a */ '-', '-', NOP, NOP, /* 4b */ 0x4b N, '4', NOP, NOP, /* 4c */ NOP, '5', NOP, NOP, /* 4d */ 0x4d N, '6', NOP, NOP, /* 4e */ '+', '+', NOP, NOP, /* 4f */ 0x4f N, '1', NOP, NOP, /* 50 */ 0x50 N, '2', NOP, NOP, /* 51 */ 0x51 N, '3', NOP, NOP, /* 52 */ 0x52 N, '0', NOP, NOP, /* 53 */ 0x53 N, '.', REBOOT, REBOOT, /* 54 */ NOP, NOP, NOP, NOP, /* SysReq */ /* 55 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 56 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 57 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 58 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 59 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 5a */ UNDEF, UNDEF, UNDEF, UNDEF, /* 5b */ UNDEF, UNDEF, UNDEF, UNDEF, /* 5c */ UNDEF, UNDEF, UNDEF, UNDEF, /* 5d */ UNDEF, UNDEF, UNDEF, UNDEF, /* 5e */ UNDEF, UNDEF, UNDEF, UNDEF, /* 5f */ UNDEF, UNDEF, UNDEF, UNDEF, /* 60 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 61 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 62 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 63 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 64 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 65 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 66 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 67 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 68 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 69 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 6a */ UNDEF, UNDEF, UNDEF, UNDEF, /* 6b */ UNDEF, UNDEF, UNDEF, UNDEF, /* 6c */ UNDEF, UNDEF, UNDEF, UNDEF, /* 6d */ UNDEF, UNDEF, UNDEF, UNDEF, /* 6e */ UNDEF, UNDEF, UNDEF, UNDEF, /* 6f */ UNDEF, UNDEF, UNDEF, UNDEF, /* 70 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 71 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 72 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 73 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 74 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 75 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 76 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 77 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 78 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 79 */ UNDEF, UNDEF, UNDEF, UNDEF, /* 7a */ UNDEF, UNDEF, UNDEF, UNDEF, /* 7b */ UNDEF, UNDEF, UNDEF, UNDEF, /* 7c */ UNDEF, UNDEF, UNDEF, UNDEF, /* 7d */ UNDEF, UNDEF, UNDEF, UNDEF, /* 7e */ UNDEF, UNDEF, UNDEF, UNDEF, /* 7f */ UNDEF, UNDEF, UNDEF, UNDEF, }; /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _BOOT_KEYBOARD_TABLE_H #define _BOOT_KEYBOARD_TABLE_H /* * Structure of the keyboard table for the bootstrap simple * keyboard driver. */ #ifdef __cplusplus extern "C" { #endif #define KBTYPE_NORMAL 0x000 /* Normal keys, process mindlessly. */ #define KBTYPE_ALPHA 0x100 /* Alpha. If CapsLock is set, swap */ /* shifted and unshifted meanings. */ /* Set this on the unshifted character */ #define KBTYPE_NUMPAD 0x200 /* Numeric/Arrow Pad. If NumLock is set, */ /* swap shifted and unshifted meanings. */ /* Set this on the unshifted character. */ #define KBTYPE_FUNC 0x300 /* Extended Function. Send this code, */ /* prefixed with zero. */ #define KBTYPE_SPEC 0x400 /* One-of-a-kind codes. Self-explanatory. */ #define KBTYPE_SPEC_NOP (KBTYPE_SPEC | 0x00) #define KBTYPE_SPEC_UNDEF (KBTYPE_SPEC | 0x01) #define KBTYPE_SPEC_LSHIFT (KBTYPE_SPEC | 0x02) #define KBTYPE_SPEC_RSHIFT (KBTYPE_SPEC | 0x03) #define KBTYPE_SPEC_CTRL (KBTYPE_SPEC | 0x04) #define KBTYPE_SPEC_ALT (KBTYPE_SPEC | 0x05) #define KBTYPE_SPEC_CAPS_LOCK (KBTYPE_SPEC | 0x06) #define KBTYPE_SPEC_NUM_LOCK (KBTYPE_SPEC | 0x07) #define KBTYPE_SPEC_SCROLL_LOCK (KBTYPE_SPEC | 0x08) #define KBTYPE_SPEC_MAYBE_REBOOT (KBTYPE_SPEC | 0x09) struct keyboard_translate { unsigned short normal; unsigned short shifted; unsigned short ctrled; unsigned short alted; }; extern struct keyboard_translate keyboard_translate[128]; #ifdef __cplusplus } #endif #endif /* _BOOT_KEYBOARD_TABLE_H */ /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING: This file is used by both dboot and the kernel. */ #include #include #include #ifdef __xpv #include #endif #ifdef _BOOT #include #define bop_panic dboot_panic #else #include #endif uint_t shift_amt_nopae[] = {12, 22}; uint_t shift_amt_pae[] = {12, 21, 30, 39}; uint_t *shift_amt; uint_t ptes_per_table; uint_t pte_size; uint32_t lpagesize; paddr_t top_page_table; uint_t top_level; /* * Return the index corresponding to a virt address at a given page table level. */ static uint_t vatoindex(uint64_t va, uint_t level) { return ((va >> shift_amt[level]) & (ptes_per_table - 1)); } /* * Return a pointer to the page table entry that maps a virtual address. * If there is no page table and probe_only is not set, one is created. */ x86pte_t * find_pte(uint64_t va, paddr_t *pa, uint_t level, uint_t probe_only) { uint_t l; uint_t index; paddr_t table; if (pa) *pa = 0; #ifndef _BOOT if (IN_HYPERVISOR_VA(va)) return (NULL); #endif /* * Walk down the page tables creating any needed intermediate tables. */ table = top_page_table; for (l = top_level; l != level; --l) { uint64_t pteval; paddr_t new_table; index = vatoindex(va, l); pteval = get_pteval(table, index); /* * Life is easy if we find the pagetable. We just use it. */ if (pteval & PT_VALID) { table = ma_to_pa(pteval & MMU_PAGEMASK); if (table == -1) { if (probe_only) return (NULL); bop_panic("find_pte(): phys not found!"); } continue; } if (probe_only) return (NULL); new_table = make_ptable(&pteval, l); set_pteval(table, index, l, pteval); table = new_table; } /* * Return a pointer into the current pagetable. */ index = vatoindex(va, l); if (pa) *pa = table + index * pte_size; return (map_pte(table, index)); } /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _BOOT_SERIAL_H #define _BOOT_SERIAL_H #ifdef __cplusplus extern "C" { #endif /* ---- ports on 16550 serial chips ---- */ #define DAT 0 /* ... data */ #define ICR 1 /* ... intr control reg */ #define ISR 2 /* ... intr status reg */ #define LCR 3 /* ... line control reg */ #define MCR 4 /* ... modem control reg */ #define LSR 5 /* ... line status reg */ #define MSR 6 /* ... modem status reg */ #define DLL 0 /* ... data latch low (used for baud rate) */ #define DLH 1 /* ... data latch high (ditto) */ #define FIFOR ISR /* ... fifo write reg */ /* ---- LSR bits ---- */ #define RCA 0x01 /* ... receive char avail */ #define XHRE 0x20 /* ... xmit hold buffer empty */ /* ---- Modem bits ---- */ #define DTR 0x01 #define RTS 0x02 #define OUT2 0x08 #define FIFO_ON 0x01 #define FIFO_OFF 0x00 #define FIFORXFLSH 0x02 #define FIFOTXFLSH 0x04 #define FIFODMA 0x08 /* ---- LCR bits ---- */ #define STOP1 00 #define STOP2 0x04 #define BITS5 0x00 /* 5 bits per char */ #define BITS6 0x01 /* 6 bits per char */ #define BITS7 0x02 /* 7 bits per char */ #define BITS8 0x03 /* 8 bits per char */ /* baud rate definitions */ #define DLAB 0x80 /* divisor latch access bit */ #define ASY110 1047 /* 110 baud rate for serial console */ #define ASY150 768 /* 150 baud rate for serial console */ #define ASY300 384 /* 300 baud rate for serial console */ #define ASY600 192 /* 600 baud rate for serial console */ #define ASY1200 96 /* 1200 baud rate for serial console */ #define ASY2400 48 /* 2400 baud rate for serial console */ #define ASY4800 24 /* 4800 baud rate for serial console */ #define ASY9600 12 /* 9600 baud rate for serial console */ #define ASY19200 6 /* 19200 baud rate for serial console */ #define ASY38400 3 /* 38400 baud rate for serial console */ #define ASY57600 2 /* 57600 baud rate for serial console */ #define ASY115200 1 /* 115200 baud rate for serial console */ /* * Defines for the serial port */ #define SERIAL_FIFO_FLUSH 16 /* maximum number of chars to flush */ /* ---- Bit 11 defines direct serial port ---- */ #define SDIRECT 0x1000 /* ---- Bits 9-10 define flow control ---- */ #define SSOFT 0x800 #define SHARD 0x400 /* ---- Bits 5-8 define baud rate ---- */ #define S110 0x00 #define S150 0x20 #define S300 0x40 #define S600 0x60 #define S1200 0x80 #define S2400 0xa0 #define S4800 0xc0 #define S9600 0xe0 #define S19200 0x100 #define S38400 0x120 #define S57600 0x140 #define S76800 0x160 #define S115200 0x180 #define S153600 0x1a0 #define S230400 0x1c0 #define S307200 0x1e0 #define S460800 0x200 /* ---- Bits 3 & 4 are parity ---- */ #define PARITY_NONE 0x10 #define PARITY_ODD 0x08 #define PARITY_EVEN 0x18 /* ---- Bit 2 is stop bit ---- */ #define STOP_1 0x00 #define STOP_2 0x04 /* ---- Bits 0 & 1 are data bits ---- */ #define DATA_8 0x03 #define DATA_7 0x02 #define DATA_6 0x01 #define DATA_5 0x00 /* ---- Line Status ---- */ #define SERIAL_TIMEOUT 0x80 #define SERIAL_XMITSHFT 0x40 #define SERIAL_XMITHOLD 0x20 #define SERIAL_BREAK 0x10 #define SERIAL_FRAME 0x08 #define SERIAL_PARITY 0x04 #define SERIAL_OVERRUN 0x02 #define SERIAL_DATA 0x01 #ifdef __cplusplus } #endif #endif /* _BOOT_SERIAL_H */ /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Miniature VGA driver for bootstrap. */ #include #include #include #include #include #include "boot_console_impl.h" #include "boot_console_impl.h" #if defined(_BOOT) #include "../dboot/dboot_asm.h" #include "../dboot/dboot_xboot.h" #endif #if defined(__xpv) && defined(_BOOT) /* * Device memory address * * In dboot under the hypervisor we don't have any memory mappings * for the first meg of low memory so we can't access devices there. * Intead we've mapped the device memory that we need to access into * a local variable within dboot so we can access the device memory * there. */ extern unsigned short *video_fb; #define VGA_SCREEN (video_fb) #else /* __xpv && _BOOT */ /* Device memory address */ #define VGA_SCREEN ((uint16_t *)(VGA_MEM_ADDR + VGA_COLOR_BASE)) #endif /* __xpv && _BOOT */ static int cons_color = CONS_COLOR; static void vga_init(void); static void vga_drawc(int); static void vga_setpos(int, int); static void vga_getpos(int *, int *); static void vga_scroll(int); static void vga_clear(int); static void vga_shiftline(int); static void vga_eraseline(void); static void vga_cursor_display(boolean_t); static void vga_set_crtc(int index, unsigned char val); static unsigned char vga_get_crtc(int index); static void vga_set_atr(int index, unsigned char val); static unsigned char vga_get_atr(int index); static int get_vga_color(void) { int color; uint32_t fg, bg; boot_get_color(&fg, &bg); color = solaris_color_to_pc_color[bg] << 4; color |= solaris_color_to_pc_color[fg]; return (color); } void boot_vga_init(bcons_dev_t *bcons_dev) { fb_info.terminal.x = VGA_TEXT_COLS; fb_info.terminal.y = VGA_TEXT_ROWS; cons_color = get_vga_color(); #if defined(_BOOT) /* * Note that we have to enable the cursor before clearing the * screen since the cursor position is dependant upon the cursor * skew, which is initialized by vga_cursor_display() */ vga_init(); fb_info.cursor.visible = B_FALSE; vga_cursor_display(B_TRUE); /* * In general we should avoid resetting the display during the boot, * we may have valueable messages there, this why the "native" loader * boot does pass the console state down to kernel and we do try to * pick the state. However, the loader is not the only way to boot. * The non-native boot loaders do not implement the smooth console. * If we have no information about cursor location, we will get value * (0, 0) and that means we better clear the screen. */ if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0) vga_clear(cons_color); vga_setpos(fb_info.cursor.pos.y, fb_info.cursor.pos.x); #endif /* _BOOT */ bcons_dev->bd_putchar = vga_drawc; bcons_dev->bd_eraseline = vga_eraseline; bcons_dev->bd_cursor = vga_cursor_display; bcons_dev->bd_setpos = vga_setpos; bcons_dev->bd_shift = vga_shiftline; } static void vga_init(void) { unsigned char val; /* set 16bit colors */ val = vga_get_atr(VGA_ATR_MODE); val &= ~VGA_ATR_MODE_BLINK; val &= ~VGA_ATR_MODE_9WIDE; vga_set_atr(VGA_ATR_MODE, val); } static void vga_cursor_display(boolean_t visible) { unsigned char val, msl; if (fb_info.cursor.visible == visible) return; /* * Figure out the maximum scan line value. We need this to set the * cursor size. */ msl = vga_get_crtc(VGA_CRTC_MAX_S_LN) & 0x1f; /* * Enable the cursor and set it's size. Preserve the upper two * bits of the control register. * - Bits 0-4 are the starting scan line of the cursor. * Scanning is done from top-to-bottom. The top-most scan * line is 0 and the bottom most scan line is the maximum scan * line value. * - Bit 5 is the cursor disable bit. */ val = vga_get_crtc(VGA_CRTC_CSSL) & 0xc0; if (visible == B_FALSE) val |= (1 << 5); vga_set_crtc(VGA_CRTC_CSSL, val); /* * Continue setting the cursors size. * - Bits 0-4 are the ending scan line of the cursor. * Scanning is done from top-to-bottom. The top-most scan * line is 0 and the bottom most scan line is the maximum scan * line value. * - Bits 5-6 are the cursor skew. */ vga_set_crtc(VGA_CRTC_CESL, msl); } static void vga_eraseline_impl(int x, int y, int color) { unsigned short val, *buf; int i; buf = VGA_SCREEN + x + y * VGA_TEXT_COLS; val = (color << 8) | ' '; for (i = x; i < VGA_TEXT_COLS; i++) buf[i] = val; } static void vga_eraseline(void) { int x, y; x = fb_info.cursor.pos.x; y = fb_info.cursor.pos.y; vga_eraseline_impl(x, y, cons_color); } static void vga_shiftline(int chars) { unsigned short *src, *dst; int x, y, len; x = fb_info.cursor.pos.x; y = fb_info.cursor.pos.y; len = VGA_TEXT_COLS - x - chars; if (len <= 0) return; src = VGA_SCREEN + x + y * VGA_TEXT_COLS; dst = src + chars; if (dst <= src) { do { *dst++ = *src++; } while (--len != 0); } else { dst += len; src += len; do { *--dst = *--src; } while (--len != 0); } } static void vga_clear(int color) { int i; for (i = 0; i < VGA_TEXT_ROWS; i++) vga_eraseline_impl(0, i, color); } static void vga_drawc(int c) { int row; int col; vga_getpos(&row, &col); if (c == '\n') { if (row < fb_info.terminal.y - 1) vga_setpos(row + 1, col); else vga_scroll(cons_color); return; } /* * VGA_SCREEN is an array of 16-bit unsigned ints, we do let * the compiler to take care of truncation here. */ VGA_SCREEN[row * VGA_TEXT_COLS + col] = (cons_color << 8) | c; if (col < VGA_TEXT_COLS - 1) vga_setpos(row, col + 1); else if (row < VGA_TEXT_ROWS - 1) vga_setpos(row + 1, 0); else { vga_setpos(row, 0); vga_scroll(cons_color); } } static void vga_scroll(int color) { int i; for (i = 0; i < (VGA_TEXT_ROWS - 1) * VGA_TEXT_COLS; i++) { VGA_SCREEN[i] = VGA_SCREEN[i + VGA_TEXT_COLS]; } vga_eraseline_impl(0, VGA_TEXT_ROWS - 1, color); } static void vga_setpos(int row, int col) { int off; if (row < 0) row = 0; if (row >= fb_info.terminal.y) row = fb_info.terminal.y - 1; if (col < 0) col = 0; if (col >= fb_info.terminal.x) col = fb_info.terminal.x - 1; off = row * VGA_TEXT_COLS + col; vga_set_crtc(VGA_CRTC_CLAH, off >> 8); vga_set_crtc(VGA_CRTC_CLAL, off & 0xff); fb_info.cursor.pos.y = row; fb_info.cursor.pos.x = col; } static void vga_getpos(int *row, int *col) { int off; off = (vga_get_crtc(VGA_CRTC_CLAH) << 8) + vga_get_crtc(VGA_CRTC_CLAL); *row = off / VGA_TEXT_COLS; *col = off % VGA_TEXT_COLS; } static void vga_set_atr(int index, unsigned char val) { (void) inb(VGA_REG_ADDR + CGA_STAT); outb(VGA_REG_ADDR + VGA_ATR_AD, index); outb(VGA_REG_ADDR + VGA_ATR_AD, val); (void) inb(VGA_REG_ADDR + CGA_STAT); outb(VGA_REG_ADDR + VGA_ATR_AD, VGA_ATR_ENB_PLT); } static unsigned char vga_get_atr(int index) { unsigned char val; (void) inb(VGA_REG_ADDR + CGA_STAT); outb(VGA_REG_ADDR + VGA_ATR_AD, index); val = inb(VGA_REG_ADDR + VGA_ATR_DATA); (void) inb(VGA_REG_ADDR + CGA_STAT); outb(VGA_REG_ADDR + VGA_ATR_AD, VGA_ATR_ENB_PLT); return (val); } static void vga_set_crtc(int index, unsigned char val) { outb(VGA_REG_ADDR + VGA_CRTC_ADR, index); outb(VGA_REG_ADDR + VGA_CRTC_DATA, val); } static unsigned char vga_get_crtc(int index) { outb(VGA_REG_ADDR + VGA_CRTC_ADR, index); return (inb(VGA_REG_ADDR + VGA_CRTC_DATA)); }