Print this page
NEX-16819 loader UEFI support
Includes work by Toomas Soome <tsoome@me.com>
Upstream commits:
    loader: pxe receive cleanup
    9475 libefi: Do not return only if ReceiveFilter
    installboot: should support efi system partition
    8931 boot1.efi: scan all display modes rather than
    loader: spinconsole updates
    loader: gfx experiment to try GOP Blt() function.
    sha1 build test
    loader: add sha1 hash calculation
    common/sha1: update for loader build
    loader: biosdisk rework
    uts: 32-bit kernel FB needs mapping in low memory
    uts: add diag-device
    uts: boot console mirror with diag-device
    uts: enable very early console on ttya
    kmdb: add diag-device as input/output device
    uts: test VGA memory exclusion from mapping
    uts: clear boot mapping and protect boot pages test
    uts: add dboot map debug printf
    uts: need to release FB pages in release_bootstrap()
    uts: add screenmap ioctl
    uts: update sys/queue.h
    loader: add illumos uts/common to include path
    loader: tem/gfx font cleanup
    loader: vbe checks
    uts: gfx_private set KD_TEXT when KD_RESETTEXT is
    uts: gfx 8-bit update
    loader: gfx 8-bit fix
    loader: always set media size from partition.
    uts: MB2 support for 32-bit kernel
    loader: x86 should have tem 80x25
    uts: x86 should have tem 80x25
    uts: font update
    loader: font update
    uts: tem attributes
    loader: tem.c comment added
    uts: use font module
    loader: add font module
    loader: build rules for new font setup
    uts: gfx_private update for new font structure
    uts: early boot update for new font structure
    uts: font update
    uts: font build rules update for new fonts
    uts: tem update to new font structure
    loader: module.c needs to include tem_impl.h
    uts: gfx_private 8x16 font rework
    uts: make font_lookup public
    loader: font rework
    uts: font rework
    9259 libefi: efi_alloc_and_read should check for PMBR
    uts: tem utf-8 support
    loader: implement tem utf-8 support
    loader: tem should be able to display UTF-8
    7784 uts: console input should support utf-8
    7796 uts: ldterm default to utf-8
    uts: do not reset serial console
    uts: set up colors even if tem is not console
    uts: add type for early boot properties
    uts: gfx_private experiment with drm and vga
    uts: gfx_private should use setmode drm callback.
    uts: identify FB types and set up gfx_private based
    loader: replace gop and vesa with framebuffer
    uts: boot needs simple tem to support mdb
    uts: boot_keyboard should emit esc sequences for
    uts: gfx_private FB showuld be written by line
    kmdb: set terminal window size
    uts: gfx_private needs to keep track of early boot FB
    pnglite: move pnglite to usr/src/common
    loader: gfx_fb
    ficl-sys: add gfx primitives
    loader: add illumos.png logo
    ficl: add fb-putimage
    loader: add png support
    loader: add alpha blending for gfx_fb
    loader: use term-drawrect for menu frame
    ficl: add simple gfx words
    uts: provide fb_info via fbgattr dev_specific array.
    uts: gfx_private add alpha blending
    uts: update sys/ascii.h
    uts: tem OSC support (incomplete)
    uts: implement env module support and use data from
    uts: tem get colors from early boot data
    loader: use crc32 from libstand (libz)
    loader: optimize for size
    loader: pass tem info to the environment
    loader: import tem for loader console
    loader: UEFI loader needs to set ISADIR based on
    loader: need UEFI32 support
    8918 loader.efi: add vesa edid support
    uts: tem_safe_pix_clear_prom_output() should only
    uts: tem_safe_pix_clear_entire_screen() should use
    uts: tem_safe_check_first_time() should query cursor
    uts: tem implement cls callback & visual_io v4
    uts: gfx_vgatext use block cursor for vgatext
    uts: gfx_private implement cls callback & visual_io
    uts: gfx_private bitmap framebuffer implementation
    uts: early start frame buffer console support
    uts: font functions should check the input char
    uts: font rendering should support 16/24/32bit depths
    uts: use smallest font as fallback default.
    uts: update terminal dimensions based on selected
    7834 uts: vgatext should use gfx_private
    uts: add spacing property to 8859-1.bdf
    terminfo: add underline for sun-color
    terminfo: sun-color has 16 colors
    uts: add font load callback type
    loader: do not repeat int13 calls with error 0x20 and
    8905 loader: add skein/edonr support
    8904 common/crypto: make skein and edonr loader
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Revert "NEX-16819 loader UEFI support"
This reverts commit ec06b9fc617b99234e538bf2e7e4d02a24993e0c.
Reverting due to failures in the zfs-tests and the sharefs-tests
NEX-16819 loader UEFI support
Includes work by Toomas Soome <tsoome@me.com>
Upstream commits:
    loader: pxe receive cleanup
    9475 libefi: Do not return only if ReceiveFilter
    installboot: should support efi system partition
    8931 boot1.efi: scan all display modes rather than
    loader: spinconsole updates
    loader: gfx experiment to try GOP Blt() function.
    sha1 build test
    loader: add sha1 hash calculation
    common/sha1: update for loader build
    loader: biosdisk rework
    uts: 32-bit kernel FB needs mapping in low memory
    uts: add diag-device
    uts: boot console mirror with diag-device
    uts: enable very early console on ttya
    kmdb: add diag-device as input/output device
    uts: test VGA memory exclusion from mapping
    uts: clear boot mapping and protect boot pages test
    uts: add dboot map debug printf
    uts: need to release FB pages in release_bootstrap()
    uts: add screenmap ioctl
    uts: update sys/queue.h
    loader: add illumos uts/common to include path
    loader: tem/gfx font cleanup
    loader: vbe checks
    uts: gfx_private set KD_TEXT when KD_RESETTEXT is
    uts: gfx 8-bit update
    loader: gfx 8-bit fix
    loader: always set media size from partition.
    uts: MB2 support for 32-bit kernel
    loader: x86 should have tem 80x25
    uts: x86 should have tem 80x25
    uts: font update
    loader: font update
    uts: tem attributes
    loader: tem.c comment added
    uts: use font module
    loader: add font module
    loader: build rules for new font setup
    uts: gfx_private update for new font structure
    uts: early boot update for new font structure
    uts: font update
    uts: font build rules update for new fonts
    uts: tem update to new font structure
    loader: module.c needs to include tem_impl.h
    uts: gfx_private 8x16 font rework
    uts: make font_lookup public
    loader: font rework
    uts: font rework
    libefi: efi_alloc_and_read should check for PMBR
    uts: tem utf-8 support
    loader: implement tem utf-8 support
    loader: tem should be able to display UTF-8
    7784 uts: console input should support utf-8
    7796 uts: ldterm default to utf-8
    uts: do not reset serial console
    uts: set up colors even if tem is not console
    uts: add type for early boot properties
    uts: gfx_private experiment with drm and vga
    uts: gfx_private should use setmode drm callback.
    uts: identify FB types and set up gfx_private based
    loader: replace gop and vesa with framebuffer
    uts: boot needs simple tem to support mdb
    uts: boot_keyboard should emit esc sequences for
    uts: gfx_private FB showuld be written by line
    kmdb: set terminal window size
    uts: gfx_private needs to keep track of early boot FB
    pnglite: move pnglite to usr/src/common
    loader: gfx_fb
    ficl-sys: add gfx primitives
    loader: add illumos.png logo
    ficl: add fb-putimage
    loader: add png support
    loader: add alpha blending for gfx_fb
    loader: use term-drawrect for menu frame
    ficl: add simple gfx words
    uts: provide fb_info via fbgattr dev_specific array.
    uts: gfx_private add alpha blending
    uts: update sys/ascii.h
    uts: tem OSC support (incomplete)
    uts: implement env module support and use data from
    uts: tem get colors from early boot data
    loader: use crc32 from libstand (libz)
    loader: optimize for size
    loader: pass tem info to the environment
    loader: import tem for loader console
    loader: UEFI loader needs to set ISADIR based on
    loader: need UEFI32 support
    8918 loader.efi: add vesa edid support
    uts: tem_safe_pix_clear_prom_output() should only
    uts: tem_safe_pix_clear_entire_screen() should use
    uts: tem_safe_check_first_time() should query cursor
    uts: tem implement cls callback & visual_io v4
    uts: gfx_vgatext use block cursor for vgatext
    uts: gfx_private implement cls callback & visual_io
    uts: gfx_private bitmap framebuffer implementation
    uts: early start frame buffer console support
    uts: font functions should check the input char
    uts: font rendering should support 16/24/32bit depths
    uts: use smallest font as fallback default.
    uts: update terminal dimensions based on selected
    7834 uts: vgatext should use gfx_private
    uts: add spacing property to 8859-1.bdf
    terminfo: add underline for sun-color
    terminfo: sun-color has 16 colors
    uts: add font load callback type
    loader: do not repeat int13 calls with error 0x20 and
    8905 loader: add skein/edonr support
    8904 common/crypto: make skein and edonr loader
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>

@@ -26,17 +26,20 @@
  */
 
 #include <sys/types.h>
 #include <sys/systm.h>
 #include <sys/archsystm.h>
+#include <sys/framebuffer.h>
 #include <sys/boot_console.h>
 #include <sys/panic.h>
 #include <sys/ctype.h>
+#include <sys/ascii.h>
 #if defined(__xpv)
 #include <sys/hypervisor.h>
 #endif /* __xpv */
 
+#include "boot_console_impl.h"
 #include "boot_serial.h"
 #include "boot_vga.h"
 
 #if defined(_BOOT)
 #include <dboot/dboot_asm.h>

@@ -55,20 +58,46 @@
 extern void bcons_putchar_xen(int);
 extern int bcons_getchar_xen(void);
 extern int bcons_ischar_xen(void);
 #endif /* __xpv */
 
-static int cons_color = CONS_COLOR;
+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);
 

@@ -90,71 +119,10 @@
                 *tnum = console_hypervisor_tty_num;
         return (console_hypervisor_device);
 }
 #endif /* __xpv */
 
-/* Clear the screen and initialize VIDEO, XPOS and YPOS. */
-void
-clear_screen(void)
-{
-        /*
-         * XXX should set vga mode so we don't depend on the
-         * state left by the boot loader.  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_cursor_display();
-        vga_clear(cons_color);
-        vga_setpos(0, 0);
-}
-
-/* Put the character C on the screen. */
-static void
-screen_putchar(int c)
-{
-        int row, col;
-
-        vga_getpos(&row, &col);
-        switch (c) {
-        case '\t':
-                col += 8 - (col % 8);
-                if (col == VGA_TEXT_COLS)
-                        col = 79;
-                vga_setpos(row, col);
-                break;
-
-        case '\r':
-                vga_setpos(row, 0);
-                break;
-
-        case '\b':
-                if (col > 0)
-                        vga_setpos(row, col - 1);
-                break;
-
-        case '\n':
-                if (row < VGA_TEXT_ROWS - 1)
-                        vga_setpos(row + 1, col);
-                else
-                        vga_scroll(cons_color);
-                break;
-
-        default:
-                vga_drawc(c, cons_color);
-                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);
-                }
-                break;
-        }
-}
-
 static int port;
 
 static void
 serial_init(void)
 {

@@ -201,17 +169,19 @@
 
         /* adjust setting based on tty properties */
         serial_adjust_prop();
 
 #if defined(_BOOT)
+#if 0
         /*
          * Do a full reset to match console behavior.
          * 0x1B + c - reset everything
          */
         serial_putchar(0x1B);
         serial_putchar('c');
 #endif
+#endif
 }
 
 /* Advance str pointer past white space */
 #define EAT_WHITE_SPACE(str)    {                       \
         while ((*str != '\0') && ISSPACE(*str))         \

@@ -603,63 +573,209 @@
 
         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 - 7 */
+        propval = find_boot_prop("tem.fg_color");
+        if (propval != NULL) {
+                intval = atoi(propval);
+                if (intval >= 0 && intval <= 7)
+                        fb_info.fg_color = intval;
+        }
+
+        /* color values are 0 - 7 */
+        propval = find_boot_prop("tem.bg_color");
+        if (propval != NULL && ISDIGIT(*propval)) {
+                intval = atoi(propval);
+                if (intval >= 0 && intval <= 7)
+                        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 console_devices array trying to match the string
+ * we were given.  The string on the command line must end with
+ * a comma or white space.
+ *
+ * Eventually we need to rework this to process dual console setup.
+ * This function does set tty_num as an side effect.
+ */
+static int
+lookup_console_devices(const char *cons_str)
+{
+        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)
+                                        tty_num = n;
+                                break;
+                        }
+                }
+        }
+        return (cons);
+}
+
 void
 bcons_init(struct xboot_info *xbi)
 {
-        console_value_t *consolep;
-        size_t len, cons_len;
         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_devices(cons_str);
+                serial_init();
+        }
+
         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
 
-        /*
-         * Go through the console_devices array trying to match the string
-         * we were given.  The string on the command line must end with
-         * a comma or white space.
-         */
-        if (cons_str != NULL) {
-                int n;
+        if (cons_str != NULL)
+                console = lookup_console_devices(cons_str);
 
-                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)) {
-                                console = consolep->value;
-                                if (console == CONS_TTY)
-                                        tty_num = n;
-                                break;
-                        }
-                }
-        }
-
 #if defined(__xpv)
         /*
          * domU's always use the hypervisor regardless of what
          * the console variable may be set to.
          */

@@ -713,10 +829,12 @@
                 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;
 

@@ -733,14 +851,13 @@
 #endif
         case CONS_SCREEN_GRAPHICS:
                 kb_init();
                 break;
         case CONS_SCREEN_TEXT:
+                boot_vga_init(&bcons_dev);
+                /* Fall through */
         default:
-#if defined(_BOOT)
-                clear_screen(); /* clears the grub or xen screen */
-#endif /* _BOOT */
                 kb_init();
                 break;
         }
 }
 

@@ -905,60 +1022,227 @@
 {
         return (inb(port + LSR) & RCA);
 }
 
 static void
-_doputchar(int c)
+btem_control(btem_state_t *btem, int c)
 {
-        switch (console) {
+        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 (c >= '0' && c <= '9') {
+                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 <ESC> sequence */
+        if (btem->btem_state != A_STATE_ESC) {
+                btem_getparams(btem, c);
+                return;
+        }
+
+        /* Previous char was <ESC> */
+        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':       /* <ESC>Q */
+        case 'C':       /* <ESC>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:
-                screen_putchar(c);
+        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)
 {
-        static int bhcharpos = 0;
-
 #if defined(__xpv)
         if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
             console == CONS_HYPERVISOR) {
                 bcons_putchar_xen(c);
                 return;
         }
 #endif /* __xpv */
 
-        if (c == '\t') {
-                do {
-                        _doputchar(' ');
-                } while (++bhcharpos % 8);
-                return;
-        } else  if (c == '\n' || c == '\r') {
-                bhcharpos = 0;
-                _doputchar('\r');
-                _doputchar(c);
-                return;
-        } else if (c == '\b') {
-                if (bhcharpos)
-                        bhcharpos--;
-                _doputchar(c);
-                return;
+        if (c == '\n') {
+                _doputchar(console, '\r');
+                if (diag != console)
+                        _doputchar(diag, '\r');
         }
-
-        bhcharpos++;
-        _doputchar(c);
+        _doputchar(console, c);
+        if (diag != console)
+                _doputchar(diag, c);
 }
 
 /*
  * kernel character input functions
  */

@@ -969,34 +1253,59 @@
         if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
             console == CONS_HYPERVISOR)
                 return (bcons_getchar_xen());
 #endif /* __xpv */
 
-        switch (console) {
-        case CONS_TTY:
+        for (;;) {
+                if (console == CONS_TTY || diag == CONS_TTY) {
+                        if (serial_ischar())
                 return (serial_getchar());
-        default:
+                }
+                if (console != CONS_INVALID || diag != CONS_INVALID) {
+                        if (kb_ischar())
                 return (kb_getchar());
         }
+        }
 }
 
 #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:
                 return (serial_ischar());
+
+        case CONS_INVALID:
+                break;
+
         default:
                 return (kb_ischar());
         }
+
+        return (c);
 }
 
 #endif /* _BOOT */