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>

@@ -23,481 +23,521 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
 
 #include <efi.h>
 #include <efilib.h>
+#include <sys/tem_impl.h>
+#include <sys/multiboot2.h>
+#include <machine/metadata.h>
+#include <gfx_fb.h>
 
 #include "bootstrap.h"
 
+struct efi_fb           efifb;
+EFI_GRAPHICS_OUTPUT     *gop;
+EFI_UGA_DRAW_PROTOCOL   *uga;
+
+static EFI_GUID ccontrol_protocol_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
+static EFI_CONSOLE_CONTROL_PROTOCOL     *console_control;
+static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
+static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
 static SIMPLE_TEXT_OUTPUT_INTERFACE     *conout;
 static SIMPLE_INPUT_INTERFACE           *conin;
+static EFI_CONSOLE_CONTROL_SCREEN_MODE  console_mode;
 
-#ifdef TERM_EMU
-#define DEFAULT_FGCOLOR EFI_LIGHTGRAY
-#define DEFAULT_BGCOLOR EFI_BLACK
+/* mode change callback and argument from tem */
+static vis_modechg_cb_t modechg_cb;
+static struct vis_modechg_arg *modechg_arg;
+static tem_vt_state_t tem;
 
-#define MAXARGS 8
 #define KEYBUFSZ 10
 static unsigned keybuf[KEYBUFSZ];      /* keybuf for extended codes */
-static int args[MAXARGS], argc;
-static int fg_c, bg_c;
-static UINTN curx, cury;
-static int esc;
 
-void get_pos(UINTN *x, UINTN *y);
-void curs_move(UINTN *_x, UINTN *_y, UINTN x, UINTN y);
-static void CL(int);
-void HO(void);
-void end_term(void);
-#endif
+static const unsigned char solaris_color_to_efi_color[16] = {
+        EFI_WHITE,
+        EFI_BLACK,
+        EFI_BLUE,
+        EFI_GREEN,
+        EFI_CYAN,
+        EFI_RED,
+        EFI_MAGENTA,
+        EFI_BROWN,
+        EFI_LIGHTGRAY,
+        EFI_DARKGRAY,
+        EFI_LIGHTBLUE,
+        EFI_LIGHTGREEN,
+        EFI_LIGHTCYAN,
+        EFI_LIGHTRED,
+        EFI_LIGHTMAGENTA,
+        EFI_YELLOW
+};
 
+#define DEFAULT_FGCOLOR EFI_LIGHTGRAY
+#define DEFAULT_BGCOLOR EFI_BLACK
+
+extern int efi_find_framebuffer(struct efi_fb *efifb);
+
+static void efi_framebuffer_setup(void);
 static void efi_cons_probe(struct console *);
 static int efi_cons_init(struct console *, int);
-void efi_cons_putchar(struct console *, int);
-int efi_cons_getchar(struct console *);
-void efi_cons_efiputchar(int);
-int efi_cons_poll(struct console *);
+static void efi_cons_putchar(struct console *, int);
+static void efi_cons_efiputchar(int);
+static int efi_cons_getchar(struct console *);
+static int efi_cons_poll(struct console *);
+static int efi_cons_ioctl(struct console *cp, int cmd, void *data);
 
+static int efi_fb_devinit(struct vis_devinit *);
+static void efi_cons_cursor(struct vis_conscursor *);
+
+static int efi_text_devinit(struct vis_devinit *);
+static int efi_text_cons_clear(struct vis_consclear *);
+static void efi_text_cons_copy(struct vis_conscopy *);
+static void efi_text_cons_display(struct vis_consdisplay *);
+
 struct console efi_console = {
-        "text",
-        "EFI console",
-        C_WIDEOUT,
-        efi_cons_probe,
-        efi_cons_init,
-        efi_cons_putchar,
-        efi_cons_getchar,
-        efi_cons_poll,
-        0
+        .c_name = "text",
+        .c_desc = "EFI console",
+        .c_flags = C_WIDEOUT,
+        .c_probe = efi_cons_probe,
+        .c_init = efi_cons_init,
+        .c_out = efi_cons_putchar,
+        .c_in = efi_cons_getchar,
+        .c_ready = efi_cons_poll,
+        .c_ioctl = efi_cons_ioctl,
+        .c_private = NULL
 };
 
-#ifdef  TERM_EMU
+static struct vis_identifier fb_ident = { "efi_fb" };
+static struct vis_identifier text_ident = { "efi_text" };
 
-/* Get cursor position. */
+struct visual_ops fb_ops = {
+        .ident = &fb_ident,
+        .kdsetmode = NULL,
+        .devinit = efi_fb_devinit,
+        .cons_copy = NULL,
+        .cons_display = NULL,
+        .cons_cursor = efi_cons_cursor,
+        .cons_clear = NULL,
+        .cons_put_cmap = NULL
+};
+
+struct visual_ops text_ops = {
+        .ident = &text_ident,
+        .kdsetmode = NULL,
+        .devinit = efi_text_devinit,
+        .cons_copy = efi_text_cons_copy,
+        .cons_display = efi_text_cons_display,
+        .cons_cursor = efi_cons_cursor,
+        .cons_clear = efi_text_cons_clear,
+        .cons_put_cmap = NULL
+};
+
+/*
+ * platform specific functions for tem
+ */
+int
+plat_stdout_is_framebuffer(void)
+{
+        return (console_mode == EfiConsoleControlScreenGraphics);
+}
+
 void
-get_pos(UINTN *x, UINTN *y)
+plat_tem_hide_prom_cursor(void)
 {
-        *x = conout->Mode->CursorColumn;
-        *y = conout->Mode->CursorRow;
+        conout->EnableCursor(conout, FALSE);
 }
 
-/* Move cursor to x rows and y cols (0-based). */
 void
-curs_move(UINTN *_x, UINTN *_y, UINTN x, UINTN y)
+plat_tem_get_prom_pos(uint32_t *row, uint32_t *col)
 {
-        conout->SetCursorPosition(conout, x, y);
-        if (_x != NULL)
-                *_x = conout->Mode->CursorColumn;
-        if (_y != NULL)
-                *_y = conout->Mode->CursorRow;
+        if (console_mode == EfiConsoleControlScreenText) {
+                *col = (uint32_t)conout->Mode->CursorColumn;
+                *row = (uint32_t)conout->Mode->CursorRow;
+        } else {
+                *col = 0;
+                *row = 0;
+        }
 }
 
-/* Clear internal state of the terminal emulation code. */
+/*
+ * plat_tem_get_prom_size() is supposed to return screen size
+ * in chars. Return real data for text mode and TEM defaults for graphical
+ * mode, so the tem can compute values based on default and font.
+ */
 void
-end_term(void)
+plat_tem_get_prom_size(size_t *height, size_t *width)
 {
-        esc = 0;
-        argc = -1;
+        UINTN cols, rows;
+        if (console_mode == EfiConsoleControlScreenText) {
+                (void) conout->QueryMode(conout, conout->Mode->Mode,
+                    &cols, &rows);
+                *height = (size_t)rows;
+                *width = (size_t)cols;
+        } else {
+                *height = TEM_DEFAULT_ROWS;
+                *width = TEM_DEFAULT_COLS;
+        }
 }
 
-#endif
-
-static void
-efi_cons_probe(struct console *cp)
+/*
+ * Callback to notify about console mode change.
+ * mode is value from enum EFI_CONSOLE_CONTROL_SCREEN_MODE.
+ */
+void
+plat_cons_update_mode(int mode)
 {
-        conout = ST->ConOut;
-        conin = ST->ConIn;
-        cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
+        EFI_STATUS status;
+        UINTN cols, rows;
+        struct vis_devinit devinit;
+
+        /* Make sure we have usable console. */
+        if (efi_find_framebuffer(&efifb)) {
+                console_mode = EfiConsoleControlScreenText;
+        } else {
+                efi_framebuffer_setup();
+                if (mode != -1 && console_mode != mode)
+                        console_mode = mode;
+        }
+
+        if (console_control != NULL)
+                (void)console_control->SetMode(console_control, console_mode);
+
+        /* some firmware enables the cursor when switching modes */
+        conout->EnableCursor(conout, FALSE);
+        if (console_mode == EfiConsoleControlScreenText) {
+                (void)conout->QueryMode(conout, conout->Mode->Mode,
+                    &cols, &rows);
+                devinit.version = VIS_CONS_REV;
+                devinit.width = cols;
+                devinit.height = rows;
+                devinit.depth = 4;
+                devinit.linebytes = cols;
+                devinit.color_map = NULL;
+                devinit.mode = VIS_TEXT;
+                efi_console.c_private = &text_ops;
+        } else {
+                devinit.version = VIS_CONS_REV;
+                devinit.width = gfx_fb.framebuffer_common.framebuffer_width;
+                devinit.height = gfx_fb.framebuffer_common.framebuffer_height;
+                devinit.depth = gfx_fb.framebuffer_common.framebuffer_bpp;
+                devinit.linebytes = gfx_fb.framebuffer_common.framebuffer_pitch;
+                devinit.color_map = gfx_fb_color_map;
+                devinit.mode = VIS_PIXEL;
+                efi_console.c_private = &fb_ops;
+        }
+
+        modechg_cb(modechg_arg, &devinit);
 }
 
 static int
-efi_cons_init(struct console *cp __attribute((unused)),
-    int arg __attribute((unused)))
+efi_fb_devinit(struct vis_devinit *data)
 {
-        conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
-            DEFAULT_BGCOLOR));
-#ifdef TERM_EMU
-        end_term();
-        get_pos(&curx, &cury);
-        curs_move(&curx, &cury, curx, cury);
-        fg_c = DEFAULT_FGCOLOR;
-        bg_c = DEFAULT_BGCOLOR;
-        memset(keybuf, 0, KEYBUFSZ);
-#endif
-        conout->EnableCursor(conout, TRUE);
-        return 0;
+        if (console_mode != EfiConsoleControlScreenGraphics)
+                return (1);
+
+        data->version = VIS_CONS_REV;
+        data->width = gfx_fb.framebuffer_common.framebuffer_width;
+        data->height = gfx_fb.framebuffer_common.framebuffer_height;
+        data->depth = gfx_fb.framebuffer_common.framebuffer_bpp;
+        data->linebytes = gfx_fb.framebuffer_common.framebuffer_pitch;
+        data->color_map = gfx_fb_color_map;
+        data->mode = VIS_PIXEL;
+
+        modechg_cb = data->modechg_cb;
+        modechg_arg = data->modechg_arg;
+
+        return (0);
 }
 
-static void
-efi_cons_rawputchar(int c)
+static int
+efi_text_devinit(struct vis_devinit *data)
 {
-        int i;
-        UINTN x, y;
-        conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
-        static int ignorenl = 0;
+        UINTN cols, rows;
 
-        if (c == '\t')
-                /* XXX lame tab expansion */
-                for (i = 0; i < 8; i++)
-                        efi_cons_rawputchar(' ');
-        else {
-#ifndef TERM_EMU
-                if (c == '\n')
-                        efi_cons_efiputchar('\r');
-                else
-                        efi_cons_efiputchar(c);
-#else
-                switch (c) {
-                case '\r':
-                        curx = 0;
-                        break;
-                case '\n':
-                        if (ignorenl)
-                                ignorenl = 0;
-                        else
-                                cury++;
-                        if ((efi_console.c_flags & C_MODERAW) == 0)
-                                curx = 0;
-                        if (cury >= y) {
-                                efi_cons_efiputchar('\n');
-                                cury--;
-                        }
-                        break;
-                case '\b':
-                        if (curx > 0)
-                                curx--;
-                        break;
-                default:
-                        if (curx > x) {
-                                curx = 0;
-                                cury++;
-                                curs_move(&curx, &cury, curx, cury);
-                        }
-                        if ((efi_console.c_flags & C_MODERAW) == 0) {
-                                if (cury > y-1) {
-                                        curx = 0;
-                                        efi_cons_efiputchar('\n');
-                                        cury--;
-                                        curs_move(&curx, &cury, curx, cury);
-                                }
-                        }
-                        efi_cons_efiputchar(c);
-                        curx++;
-                        if ((efi_console.c_flags & C_MODERAW) == 0) {
-                                if (curx == x) {
-                                        curx = 0;
-                                        ignorenl = 1;
-                                }
-                        } else if (curx == x) {
-                                curx = 0;
-                                if (cury == y)
-                                        efi_cons_efiputchar('\n');
-                                else
-                                        cury++;
-                        }
-                }
-                curs_move(&curx, &cury, curx, cury);
-#endif
-        }
+        if (console_mode != EfiConsoleControlScreenText)
+                return (1);
+
+        (void)conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
+        data->version = VIS_CONS_REV;
+        data->width = cols;
+        data->height = rows;
+        data->depth = 4;
+        data->linebytes = cols;
+        data->color_map = NULL;
+        data->mode = VIS_TEXT;
+
+        modechg_cb = data->modechg_cb;
+        modechg_arg = data->modechg_arg;
+
+        return (0);
 }
 
-/* Gracefully exit ESC-sequence processing in case of misunderstanding. */
-static void
-bail_out(int c)
+static int
+efi_text_cons_clear(struct vis_consclear *ca)
 {
-        char buf[16], *ch;
-        int i;
+        EFI_STATUS st;
+        UINTN attr = conout->Mode->Attribute & 0x0F;
 
-        if (esc) {
-                efi_cons_rawputchar('\033');
-                if (esc != '\033')
-                        efi_cons_rawputchar(esc);
-                for (i = 0; i <= argc; ++i) {
-                        sprintf(buf, "%d", args[i]);
-                        ch = buf;
-                        while (*ch)
-                                efi_cons_rawputchar(*ch++);
-                }
-        }
-        efi_cons_rawputchar(c);
-        end_term();
+        attr = EFI_TEXT_ATTR(attr,
+            solaris_color_to_efi_color[ca->bg_color & 0xF]);
+        st = conout->SetAttribute(conout, attr);
+        if (EFI_ERROR(st))
+                return (1);
+        st = conout->ClearScreen(conout);
+        if (EFI_ERROR(st))
+                return (1);
+        return (0);
 }
 
-/* Clear display from current position to end of screen. */
 static void
-CD(void) {
-        UINTN i, x, y;
+efi_text_cons_copy(struct vis_conscopy *ma)
+{
+        UINTN col, row;
 
-        get_pos(&curx, &cury);
-        if (curx == 0 && cury == 0) {
-                conout->ClearScreen(conout);
-                end_term();
-                return;
-        }
+        col = 0;
+        row = ma->e_row;
+        conout->SetCursorPosition(conout, col, row);
 
-        conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
-        CL(0);  /* clear current line from cursor to end */
-        for (i = cury + 1; i < y-1; i++) {
-                curs_move(NULL, NULL, 0, i);
-                CL(0);
-        }
-        curs_move(NULL, NULL, curx, cury);
-        end_term();
+        efi_cons_efiputchar('\n');
 }
 
-/*
- * Absolute cursor move to args[0] rows and args[1] columns
- * (the coordinates are 1-based).
- */
 static void
-CM(void)
+efi_text_cons_display(struct vis_consdisplay *da)
 {
-        if (args[0] > 0)
-                args[0]--;
-        if (args[1] > 0)
-                args[1]--;
-        curs_move(&curx, &cury, args[1], args[0]);
-        end_term();
+        EFI_STATUS st;
+        UINTN attr;
+        UINTN row, col;
+        tem_char_t *data;
+        int i;
+
+        (void)conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
+
+        /* reduce clear line on bottom row by one to prevent autoscroll */
+        if (row - 1 == da->row && da->col == 0 && da->width == col)
+                da->width--;
+
+        data = (tem_char_t *)da->data;
+        attr = EFI_TEXT_ATTR(solaris_color_to_efi_color[da->fg_color & 0xf],
+            solaris_color_to_efi_color[da->bg_color & 0xf]);
+        st = conout->SetAttribute(conout, attr);
+        if (EFI_ERROR(st))
+                return;
+        row = da->row;
+        col = da->col;
+        conout->SetCursorPosition(conout, col, row);
+        for (i = 0; i < da->width; i++)
+                efi_cons_efiputchar(data[i]);
 }
 
-/* Home cursor (left top corner), also called from mode command. */
-void
-HO(void)
+static void efi_cons_cursor(struct vis_conscursor *cc)
 {
-        argc = 1;
-        args[0] = args[1] = 1;
-        CM();
+        switch (cc->action) {
+        case VIS_HIDE_CURSOR:
+                if (plat_stdout_is_framebuffer())
+                        gfx_fb_display_cursor(cc);
+                else
+                        plat_tem_hide_prom_cursor();
+                break;
+        case VIS_DISPLAY_CURSOR:
+                if (plat_stdout_is_framebuffer())
+                        gfx_fb_display_cursor(cc);
+                else {
+                        UINTN row, col;
+                        row = cc->row;
+                        col = cc->col;
+                        conout->SetCursorPosition(conout, col, row);
+                        conout->EnableCursor(conout, TRUE);
+                }
+                break;
+        case VIS_GET_CURSOR: {  /* only used at startup */
+                uint32_t row, col;
+
+                plat_tem_get_prom_pos(&row, &col);
+                cc->row = row;
+                cc->col = col;
+                }
+                break;
+        }
 }
 
-/* Clear line from current position to end of line */
-static void
-CL(int direction)
+static int
+efi_cons_ioctl(struct console *cp, int cmd, void *data)
 {
-        int i, len;
-        UINTN x, y;
-        CHAR16 *line;
+        struct visual_ops *ops = cp->c_private;
 
-        conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
-        switch (direction) {
-        case 0:         /* from cursor to end */
-                len = x - curx + 1;
+        switch (cmd) {
+        case VIS_GETIDENTIFIER:
+                memmove(data, ops->ident, sizeof (struct vis_identifier));
                 break;
-        case 1:         /* from beginning to cursor */
-                len = curx;
+        case VIS_DEVINIT:
+                return (ops->devinit(data));
+        case VIS_CONSCLEAR:
+                return (ops->cons_clear(data));
+        case VIS_CONSCOPY:
+                ops->cons_copy(data);
                 break;
-        case 2:         /* entire line */
-        default:
-                len = x;
+        case VIS_CONSDISPLAY:
+                ops->cons_display(data);
                 break;
+        case VIS_CONSCURSOR:
+                ops->cons_cursor(data);
+                break;
+        default:
+                return (EINVAL);
         }
+        return (0);
+}
 
-        if (cury == y - 1)
-                len--;
+static void
+efi_framebuffer_setup(void)
+{
+        int bpp, pos;
 
-        line = malloc(len * sizeof (CHAR16));
-        if (line == NULL) {
-                printf("out of memory\n");
-                return;
-        }
-        for (i = 0; i < len; i++)
-                line[i] = ' ';
-        line[len-1] = 0;
+        bpp = fls(efifb.fb_mask_red | efifb.fb_mask_green |
+            efifb.fb_mask_blue | efifb.fb_mask_reserved);
 
-        if (direction != 0)
-                curs_move(NULL, NULL, 0, cury);
+        gfx_fb.framebuffer_common.mb_type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
+        gfx_fb.framebuffer_common.mb_size = sizeof (gfx_fb);
+        gfx_fb.framebuffer_common.framebuffer_addr = efifb.fb_addr;
+        gfx_fb.framebuffer_common.framebuffer_width = efifb.fb_width;
+        gfx_fb.framebuffer_common.framebuffer_height = efifb.fb_height;
+        gfx_fb.framebuffer_common.framebuffer_bpp = bpp;
+        gfx_fb.framebuffer_common.framebuffer_pitch =
+            efifb.fb_stride * (bpp >> 3);
+        gfx_fb.framebuffer_common.framebuffer_type =
+            MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
+        gfx_fb.framebuffer_common.mb_reserved = 0;
 
-        conout->OutputString(conout, line);
-        /* restore cursor position */
-        curs_move(NULL, NULL, curx, cury);
-        free(line);
-        end_term();
+        pos = ffs(efifb.fb_mask_red);
+        if (pos != 0)
+                pos--;
+        gfx_fb.u.fb2.framebuffer_red_mask_size = fls(efifb.fb_mask_red >> pos);
+        gfx_fb.u.fb2.framebuffer_red_field_position = pos;
+        pos = ffs(efifb.fb_mask_green);
+        if (pos != 0)
+                pos--;
+        gfx_fb.u.fb2.framebuffer_green_mask_size =
+            fls(efifb.fb_mask_green >> pos);
+        gfx_fb.u.fb2.framebuffer_green_field_position = pos;
+        pos = ffs(efifb.fb_mask_blue);
+        if (pos != 0)
+                pos--;
+        gfx_fb.u.fb2.framebuffer_blue_mask_size =
+            fls(efifb.fb_mask_blue >> pos);
+        gfx_fb.u.fb2.framebuffer_blue_field_position = pos;
 }
 
 static void
-get_arg(int c)
+efi_cons_probe(struct console *cp)
 {
-        if (argc < 0)
-                argc = 0;
-        args[argc] *= 10;
-        args[argc] += c - '0';
-}
+        EFI_STATUS status;
+        UINTN i, max_dim, best_mode, cols, rows;
 
-/* Emulate basic capabilities of sun-color terminal */
-static void
-efi_term_emu(int c)
-{
-        static int ansi_col[] = {
-                0, 4, 2, 6, 1, 5, 3, 7
-        };
-        int t, i;
+        conout = ST->ConOut;
+        conin = ST->ConIn;
+        cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
 
-        switch (esc) {
-        case 0:
-                switch (c) {
-                case '\033':
-                        esc = c;
-                        break;
-                default:
-                        efi_cons_rawputchar(c);
-                        break;
+        status = BS->LocateProtocol(&ccontrol_protocol_guid, NULL,
+            (VOID **)&console_control);
+        if (status == EFI_SUCCESS) {
+                BOOLEAN GopUgaExists, StdInLocked;
+                status = console_control->GetMode(console_control,
+                    &console_mode, &GopUgaExists, &StdInLocked);
+        } else {
+                console_mode = EfiConsoleControlScreenText;
                 }
-                break;
-        case '\033':
-                switch (c) {
-                case '[':
-                        esc = c;
-                        args[0] = 0;
-                        argc = -1;
-                        break;
-                default:
-                        bail_out(c);
-                        break;
+
+        max_dim = best_mode = 0;
+        for (i = 0; i <= conout->Mode->MaxMode ; i++) {
+                status = conout->QueryMode(conout, i, &cols, &rows);
+                if (EFI_ERROR(status))
+                        continue;
+                if (cols * rows > max_dim) {
+                        max_dim = cols * rows;
+                        best_mode = i;
                 }
-                break;
-        case '[':
-                switch (c) {
-                case ';':
-                        if (argc < 0)
-                                argc = 0;
-                        else if (argc + 1 >= MAXARGS)
-                                bail_out(c);
-                        else
-                                args[++argc] = 0;
-                        break;
-                case 'A':               /* UP = \E[%dA */
-                        if (argc == 0) {
-                                UINTN x, y;
-                                get_pos(&x, &y);
-                                args[1] = x + 1;
-                                args[0] = y - args[0] + 1;
-                                CM();
-                        } else
-                                bail_out(c);
-                        break;
-                case 'B':               /* DO = \E[%dB */
-                        if (argc == 0) {
-                                UINTN x, y;
-                                get_pos(&x, &y);
-                                args[1] = x + 1;
-                                args[0] = y + args[0] + 1;
-                                CM();
-                        } else
-                                bail_out(c);
-                        break;
-                case 'C':               /* RI = \E[%dC */
-                        if (argc == 0) {
-                                UINTN x, y;
-                                get_pos(&x, &y);
-                                args[1] = args[0] + 1;
-                                args[0] = y + 1;
-                                CM();
-                        } else
-                                bail_out(c);
-                        break;
-                case 'H':               /* ho = \E[H */
-                        if (argc < 0)
-                                HO();
-                        else if (argc == 1)
-                                CM();
-                        else
-                                bail_out(c);
-                        break;
-                case 'J':               /* cd = \E[J */
-                        if (argc < 0)
-                                CD();
-                        else
-                                bail_out(c);
-                        break;
-                case 'K':
-                        if (argc < 0)
-                                CL(0);
-                        else if (argc == 0)
-                                switch (args[0]) {
-                                case 0:
-                                case 1:
-                                case 2:
-                                        CL(args[0]);
-                                break;
-                                default:
-                                        bail_out(c);
                                 }
-                        else
-                                bail_out(c);
-                        break;
-                case 'm':
-                        if (argc < 0) {
-                                fg_c = DEFAULT_FGCOLOR;
-                                bg_c = DEFAULT_BGCOLOR;
+        if (max_dim > 0)
+                conout->SetMode(conout, best_mode);
+        status = conout->QueryMode(conout, best_mode, &cols, &rows);
+        if (EFI_ERROR(status)) {
+                setenv("screen-#rows", "24", 1);
+                setenv("screen-#cols", "80", 1);
+        } else {
+                char env[8];
+                sprintf(env, "%u", (unsigned)rows);
+                setenv("screen-#rows", env, 1);
+                sprintf(env, "%u", (unsigned)cols);
+                setenv("screen-#cols", env, 1);
                         }
-                        for (i = 0; i <= argc; ++i) {
-                                switch (args[i]) {
-                                case 0:         /* back to normal */
-                                        fg_c = DEFAULT_FGCOLOR;
-                                        bg_c = DEFAULT_BGCOLOR;
-                                        break;
-                                case 1:         /* bold */
-                                        fg_c |= 0x8;
-                                        break;
-                                case 4:         /* underline */
-                                case 5:         /* blink */
-                                        bg_c |= 0x8;
-                                        break;
-                                case 7:         /* reverse */
-                                        t = fg_c;
-                                        fg_c = bg_c;
-                                        bg_c = t;
-                                        break;
-                                case 30: case 31: case 32: case 33:
-                                case 34: case 35: case 36: case 37:
-                                        fg_c = ansi_col[args[i] - 30];
-                                        break;
-                                case 39:        /* normal */
-                                        fg_c = DEFAULT_FGCOLOR;
-                                        break;
-                                case 40: case 41: case 42: case 43:
-                                case 44: case 45: case 46: case 47:
-                                        bg_c = ansi_col[args[i] - 40];
-                                        break;
-                                case 49:        /* normal */
-                                        bg_c = DEFAULT_BGCOLOR;
-                                        break;
+
+        if (efi_find_framebuffer(&efifb)) {
+                console_mode = EfiConsoleControlScreenText;
+                cp->c_private = &text_ops;
+        } else {
+                efi_framebuffer_setup();
+                console_mode = EfiConsoleControlScreenGraphics;
+                cp->c_private = &fb_ops;
                                 }
-                        }
-                        conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
-                        end_term();
-                        break;
-                default:
-                        if (isdigit(c))
-                                get_arg(c);
-                        else
-                                bail_out(c);
-                        break;
-                }
-                break;
-        default:
-                bail_out(c);
-                break;
-        }
+
+        if (console_control != NULL)
+                (void)console_control->SetMode(console_control, console_mode);
+
+        /* some firmware enables the cursor when switching modes */
+        conout->EnableCursor(conout, FALSE);
 }
 
-void
-efi_cons_putchar(struct console *cp __attribute((unused)), int c)
+static int
+efi_cons_init(struct console *cp, int arg __unused)
 {
-#ifdef TERM_EMU
-        efi_term_emu(c);
-#else
-        efi_cons_rawputchar(c);
+        EFI_STATUS status;
+        int rc;
+
+        conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
+            DEFAULT_BGCOLOR));
+        memset(keybuf, 0, KEYBUFSZ);
+#if 0
+        status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
+            (void**)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 #endif
+        coninex = NULL;
+
+        gfx_framework_init(&fb_ops);
+        rc = tem_info_init(cp);
+
+        if (rc == 0 && tem == NULL) {
+                tem = tem_init();
+                if (tem != NULL)
+                        tem_activate(tem, B_TRUE);
+        }
+
+        if (tem == NULL)
+                panic("Failed to set up console terminal");
+
+        return 0;
 }
 
-int
-efi_cons_getchar(struct console *cp __attribute((unused)))
+static void
+efi_cons_putchar(struct console *cp __unused, int c)
 {
-        EFI_INPUT_KEY key;
+        uint8_t buf = c;
+
+        /* make sure we have some console output, support for panic() */
+        if (tem == NULL)
+                efi_cons_efiputchar(c);
+        else
+                tem_write(tem, &buf, sizeof (buf));
+}
+
+static int
+efi_cons_getchar(struct console *cp __unused)
+{
+        EFI_INPUT_KEY key, *kp;
+        EFI_KEY_DATA  key_data;
         EFI_STATUS status;
         UINTN junk;
         int i, c;
 
         for (i = 0; i < KEYBUFSZ; i++) {

@@ -506,18 +546,31 @@
                         keybuf[i] = 0;
                         return (c);
                 }
         }
 
-        /* Try to read a key stroke. We wait for one if none is pending. */
+        if (coninex != NULL) {
+                status = coninex->ReadKeyStrokeEx(coninex, &key_data);
+                if (status == EFI_NOT_READY) {
+                        BS->WaitForEvent(1, &coninex->WaitForKeyEx, &junk);
+                        status = coninex->ReadKeyStrokeEx(coninex, &key_data);
+                }
+                kp = &key_data.Key;
+        } else {
         status = conin->ReadKeyStroke(conin, &key);
         if (status == EFI_NOT_READY) {
                 BS->WaitForEvent(1, &conin->WaitForKey, &junk);
                 status = conin->ReadKeyStroke(conin, &key);
         }
+                kp = &key;
+        }
 
-        switch (key.ScanCode) {
+        if (status != EFI_SUCCESS) {
+                return (-1);
+        }
+
+        switch (kp->ScanCode) {
         case 0x1: /* UP */
                 keybuf[0] = '[';
                 keybuf[1] = 'A';
                 return (0x1b);  /* esc */
         case 0x2: /* DOWN */

@@ -535,55 +588,40 @@
         case 0x17: /* ESC */
                 return (0x1b);  /* esc */
         }
 
         /* this can return  */
-        return (key.UnicodeChar);
+        return (kp->UnicodeChar);
 }
 
-int
-efi_cons_poll(struct console *cp __attribute((unused)))
+static int
+efi_cons_poll(struct console *cp __unused)
 {
         int i;
 
         for (i = 0; i < KEYBUFSZ; i++) {
                 if (keybuf[i] != 0)
                         return (1);
         }
 
         /* This can clear the signaled state. */
+        if (coninex != NULL)
+                return (BS->CheckEvent(coninex->WaitForKeyEx) == EFI_SUCCESS);
+        else
         return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
 }
 
 /* Plain direct access to EFI OutputString(). */
 void
 efi_cons_efiputchar(int c)
 {
         CHAR16 buf[2];
+        EFI_STATUS status;
 
-        /*
-         * translate box chars to unicode
-         */
-        switch (c) {
-        /* single frame */
-        case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
-        case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
-        case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
-        case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
-        case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
-        case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
-
-        /* double frame */
-        case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
-        case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
-        case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
-        case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
-        case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
-        case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
-
-        default:
                 buf[0] = c;
-        }
         buf[1] = 0;     /* terminate string */
 
+        status = conout->TestString(conout, buf);
+        if (EFI_ERROR(status))
+                buf[0] = '?';
         conout->OutputString(conout, buf);
 }