diff options
Diffstat (limited to 'stand/efi/libefi/efi_console.c')
-rw-r--r-- | stand/efi/libefi/efi_console.c | 484 |
1 files changed, 325 insertions, 159 deletions
diff --git a/stand/efi/libefi/efi_console.c b/stand/efi/libefi/efi_console.c index a72fb4d561da..22adcd3a00e2 100644 --- a/stand/efi/libefi/efi_console.c +++ b/stand/efi/libefi/efi_console.c @@ -27,17 +27,22 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include <sys/param.h> #include <efi.h> #include <efilib.h> #include <teken.h> #include <sys/reboot.h> - +#include <machine/metadata.h> +#include <gfx_fb.h> #include "bootstrap.h" +extern EFI_GUID gop_guid; +extern int efi_find_framebuffer(struct efi_fb *efifb); static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; static SIMPLE_INPUT_INTERFACE *conin; static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex; +static bool efi_started; static int mode; /* Does ConOut have serial console? */ @@ -59,6 +64,9 @@ void HO(void); void end_term(void); #endif +#define TEXT_ROWS 24 +#define TEXT_COLS 80 + static tf_bell_t efi_cons_bell; static tf_cursor_t efi_text_cursor; static tf_putchar_t efi_text_putchar; @@ -77,16 +85,16 @@ static teken_funcs_t tf = { .tf_respond = efi_cons_respond, }; -teken_t teken; -teken_pos_t tp; - -struct text_pixel { - teken_char_t c; - teken_attr_t a; +static teken_funcs_t tfx = { + .tf_bell = efi_cons_bell, + .tf_cursor = gfx_fb_cursor, + .tf_putchar = gfx_fb_putchar, + .tf_fill = gfx_fb_fill, + .tf_copy = gfx_fb_copy, + .tf_param = gfx_fb_param, + .tf_respond = efi_cons_respond, }; -static struct text_pixel *buffer; - #define KEYBUFSZ 10 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ static int key_pending; @@ -116,6 +124,7 @@ void efi_cons_putchar(int); int efi_cons_getchar(void); void efi_cons_efiputchar(int); int efi_cons_poll(void); +static void cons_draw_frame(teken_attr_t *); struct console efi_console = { "efi", @@ -129,6 +138,28 @@ struct console efi_console = { }; /* + * This function is used to mark a rectangular image area so the scrolling + * will know we need to copy the data from there. + */ +void +term_image_display(teken_gfx_t *state, const teken_rect_t *r) +{ + teken_pos_t p; + int idx; + + for (p.tp_row = r->tr_begin.tp_row; + p.tp_row < r->tr_end.tp_row; p.tp_row++) { + for (p.tp_col = r->tr_begin.tp_col; + p.tp_col < r->tr_end.tp_col; p.tp_col++) { + idx = p.tp_col + p.tp_row * state->tg_tp.tp_col; + if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) + return; + screen_buffer[idx].a.ta_format |= TF_IMAGE; + } + } +} + +/* * Not implemented. */ static void @@ -137,33 +168,30 @@ efi_cons_bell(void *s __unused) } static void -efi_text_cursor(void *s __unused, const teken_pos_t *p) +efi_text_cursor(void *arg, const teken_pos_t *p) { - UINTN row, col; + teken_gfx_t *state = arg; + UINTN col, row; - (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); + row = p->tp_row; + if (p->tp_row >= state->tg_tp.tp_row) + row = state->tg_tp.tp_row - 1; - if (p->tp_col == col) - col = p->tp_col - 1; - else - col = p->tp_col; - - if (p->tp_row == row) - row = p->tp_row - 1; - else - row = p->tp_row; + col = p->tp_col; + if (p->tp_col >= state->tg_tp.tp_col) + col = state->tg_tp.tp_col - 1; conout->SetCursorPosition(conout, col, row); } static void -efi_text_printchar(const teken_pos_t *p, bool autoscroll) +efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll) { UINTN a, attr; struct text_pixel *px; teken_color_t fg, bg, tmp; - px = buffer + p->tp_col + p->tp_row * tp.tp_col; + px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col; a = conout->Mode->Attribute; fg = teken_256to16(px->a.ta_fgcolor); @@ -184,10 +212,10 @@ efi_text_printchar(const teken_pos_t *p, bool autoscroll) conout->SetCursorPosition(conout, p->tp_col, p->tp_row); - /* to prvent autoscroll, skip print of lower right char */ + /* to prevent autoscroll, skip print of lower right char */ if (!autoscroll && - p->tp_row == tp.tp_row - 1 && - p->tp_col == tp.tp_col - 1) + p->tp_row == state->tg_tp.tp_row - 1 && + p->tp_col == state->tg_tp.tp_col - 1) return; (void) conout->SetAttribute(conout, attr); @@ -196,58 +224,80 @@ efi_text_printchar(const teken_pos_t *p, bool autoscroll) } static void -efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c, +efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c, const teken_attr_t *a) { + teken_gfx_t *state = s; EFI_STATUS status; int idx; - idx = p->tp_col + p->tp_row * tp.tp_col; - buffer[idx].c = c; - buffer[idx].a = *a; - efi_text_printchar(p, false); + idx = p->tp_col + p->tp_row * state->tg_tp.tp_col; + if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) + return; + + screen_buffer[idx].c = c; + screen_buffer[idx].a = *a; + + efi_text_printchar(s, p, false); } static void -efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c, +efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c, const teken_attr_t *a) { + teken_gfx_t *state = arg; teken_pos_t p; - UINTN row, col; - - (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); - conout->EnableCursor(conout, FALSE); + if (state->tg_cursor_visible) + conout->EnableCursor(conout, FALSE); for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; p.tp_row++) for (p.tp_col = r->tr_begin.tp_col; p.tp_col < r->tr_end.tp_col; p.tp_col++) - efi_text_putchar(s, &p, c, a); - conout->EnableCursor(conout, TRUE); + efi_text_putchar(state, &p, c, a); + if (state->tg_cursor_visible) + conout->EnableCursor(conout, TRUE); } -static bool -efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2) +static void +efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, + teken_pos_t *d, bool scroll) { - if (px1->c != px2->c) - return (false); - - if (px1->a.ta_format != px2->a.ta_format) - return (false); - if (px1->a.ta_fgcolor != px2->a.ta_fgcolor) - return (false); - if (px1->a.ta_bgcolor != px2->a.ta_bgcolor) - return (false); - - return (true); + unsigned soffset, doffset; + teken_pos_t sp, dp; + int x; + + soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col; + doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col; + + sp = *s; + dp = *d; + for (x = 0; x < ncol; x++) { + sp.tp_col = s->tp_col + x; + dp.tp_col = d->tp_col + x; + if (!is_same_pixel(&screen_buffer[soffset + x], + &screen_buffer[doffset + x])) { + screen_buffer[doffset + x] = + screen_buffer[soffset + x]; + if (!scroll) + efi_text_printchar(state, &dp, false); + } else if (scroll) { + /* Draw last char and trigger scroll. */ + if (dp.tp_col + 1 == state->tg_tp.tp_col && + dp.tp_row + 1 == state->tg_tp.tp_row) { + efi_text_printchar(state, &dp, true); + } + } + } } static void -efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p) +efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p) { - int srow, drow; - int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ + teken_gfx_t *state = arg; + unsigned doffset, soffset; teken_pos_t d, s; + int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ bool scroll = false; /* @@ -262,90 +312,47 @@ efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p) * Check if we do copy whole screen. */ if (p->tp_row == 0 && p->tp_col == 0 && - nrow == tp.tp_row - 2 && ncol == tp.tp_col - 2) + nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2) scroll = true; - conout->EnableCursor(conout, FALSE); - if (p->tp_row < r->tr_begin.tp_row) { - /* Copy from bottom to top. */ + soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col; + doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col; + + /* remove the cursor */ + if (state->tg_cursor_visible) + conout->EnableCursor(conout, FALSE); + + /* + * Copy line by line. + */ + if (doffset <= soffset) { + s = r->tr_begin; + d = *p; for (y = 0; y < nrow; y++) { - d.tp_row = p->tp_row + y; s.tp_row = r->tr_begin.tp_row + y; - drow = d.tp_row * tp.tp_col; - srow = s.tp_row * tp.tp_col; - for (x = 0; x < ncol; x++) { - d.tp_col = p->tp_col + x; - s.tp_col = r->tr_begin.tp_col + x; - - if (!efi_same_pixel( - &buffer[d.tp_col + drow], - &buffer[s.tp_col + srow])) { - buffer[d.tp_col + drow] = - buffer[s.tp_col + srow]; - if (!scroll) - efi_text_printchar(&d, false); - } else if (scroll) { - /* - * Draw last char and trigger - * scroll. - */ - if (y == nrow - 1 && - x == ncol - 1) { - efi_text_printchar(&d, true); - } - } - } + d.tp_row = p->tp_row + y; + + efi_text_copy_line(state, ncol, &s, &d, scroll); } } else { - /* Copy from top to bottom. */ - if (p->tp_col < r->tr_begin.tp_col) { - /* Copy from right to left. */ - for (y = nrow - 1; y >= 0; y--) { - d.tp_row = p->tp_row + y; - s.tp_row = r->tr_begin.tp_row + y; - drow = d.tp_row * tp.tp_col; - srow = s.tp_row * tp.tp_col; - for (x = 0; x < ncol; x++) { - d.tp_col = p->tp_col + x; - s.tp_col = r->tr_begin.tp_col + x; - - if (!efi_same_pixel( - &buffer[d.tp_col + drow], - &buffer[s.tp_col + srow])) { - buffer[d.tp_col + drow] = - buffer[s.tp_col + srow]; - efi_text_printchar(&d, false); - } - } - } - } else { - /* Copy from left to right. */ - for (y = nrow - 1; y >= 0; y--) { - d.tp_row = p->tp_row + y; - s.tp_row = r->tr_begin.tp_row + y; - drow = d.tp_row * tp.tp_col; - srow = s.tp_row * tp.tp_col; - for (x = ncol - 1; x >= 0; x--) { - d.tp_col = p->tp_col + x; - s.tp_col = r->tr_begin.tp_col + x; - - if (!efi_same_pixel( - &buffer[d.tp_col + drow], - &buffer[s.tp_col + srow])) { - buffer[d.tp_col + drow] = - buffer[s.tp_col + srow]; - efi_text_printchar(&d, false); - } - } - } + for (y = nrow - 1; y >= 0; y--) { + s.tp_row = r->tr_begin.tp_row + y; + d.tp_row = p->tp_row + y; + + efi_text_copy_line(state, ncol, &s, &d, false); } } - conout->EnableCursor(conout, TRUE); + + /* display the cursor */ + if (state->tg_cursor_visible) + conout->EnableCursor(conout, TRUE); } static void -efi_text_param(void *s __unused, int cmd, unsigned int value) +efi_text_param(void *arg, int cmd, unsigned int value) { + teken_gfx_t *state = arg; + switch (cmd) { case TP_SETLOCALCURSOR: /* @@ -357,10 +364,13 @@ efi_text_param(void *s __unused, int cmd, unsigned int value) value = (value == 1) ? 0 : 1; /* FALLTHROUGH */ case TP_SHOWCURSOR: - if (value == 1) + if (value != 0) { conout->EnableCursor(conout, TRUE); - else + state->tg_cursor_visible = true; + } else { conout->EnableCursor(conout, FALSE); + state->tg_cursor_visible = false; + } break; default: /* Not yet implemented */ @@ -470,7 +480,7 @@ efi_set_colors(struct env_var *ev, int flags, const void *value) evalue = value; } - ap = teken_get_defattr(&teken); + ap = teken_get_defattr(&gfx_state.tg_teken); a = *ap; if (strcmp(ev->ev_name, "teken.fg_color") == 0) { /* is it already set? */ @@ -484,8 +494,15 @@ efi_set_colors(struct env_var *ev, int flags, const void *value) return (CMD_OK); a.ta_bgcolor = val; } + + /* Improve visibility */ + if (a.ta_bgcolor == TC_WHITE) + a.ta_bgcolor |= TC_LIGHT; + + teken_set_defattr(&gfx_state.tg_teken, &a); + cons_draw_frame(&a); env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); - teken_set_defattr(&teken, &a); + teken_input(&gfx_state.tg_teken, "\e[2J", 4); return (CMD_OK); } @@ -823,19 +840,100 @@ efi_term_emu(int c) #endif } +static int +env_screen_nounset(struct env_var *ev __unused) +{ + if (gfx_state.tg_fb_type == FB_TEXT) + return (0); + return (EPERM); +} + +static void +cons_draw_frame(teken_attr_t *a) +{ + teken_attr_t attr = *a; + teken_color_t fg = a->ta_fgcolor; + + attr.ta_fgcolor = attr.ta_bgcolor; + teken_set_defattr(&gfx_state.tg_teken, &attr); + + gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width, + gfx_state.tg_origin.tp_row, 1); + gfx_fb_drawrect(0, + gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, + gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1); + gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row, + gfx_state.tg_origin.tp_col, + gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1); + gfx_fb_drawrect( + gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1, + gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width, + gfx_state.tg_fb.fb_height, 1); + + attr.ta_fgcolor = fg; + teken_set_defattr(&gfx_state.tg_teken, &attr); +} + bool -efi_cons_update_mode(void) +cons_update_mode(bool use_gfx_mode) { UINTN cols, rows; const teken_attr_t *a; teken_attr_t attr; EFI_STATUS status; - char env[8], *ptr; + EFI_GRAPHICS_OUTPUT *gop = NULL; + struct efi_fb efifb; + char env[10], *ptr; + + if (use_gfx_mode == true) { + gfx_state.tg_fb_type = FB_GOP; + if (gfx_state.tg_private == NULL) { + (void) BS->LocateProtocol(&gop_guid, NULL, + (void **)&gop); + gfx_state.tg_private = gop; + } else { + gop = gfx_state.tg_private; + } + + /* + * We have FB but no GOP - it must be UGA. + */ + if (gop == NULL) + gfx_state.tg_fb_type = FB_UGA; + + if (efi_find_framebuffer(&efifb) == 0) { + int roff, goff, boff; + + gfx_state.tg_fb.fb_addr = efifb.fb_addr; + gfx_state.tg_fb.fb_size = efifb.fb_size; + gfx_state.tg_fb.fb_height = efifb.fb_height; + gfx_state.tg_fb.fb_width = efifb.fb_width; + gfx_state.tg_fb.fb_stride = efifb.fb_stride; + gfx_state.tg_fb.fb_mask_red = efifb.fb_mask_red; + gfx_state.tg_fb.fb_mask_green = efifb.fb_mask_green; + gfx_state.tg_fb.fb_mask_blue = efifb.fb_mask_blue; + gfx_state.tg_fb.fb_mask_reserved = + efifb.fb_mask_reserved; + roff = ffs(efifb.fb_mask_red) - 1; + goff = ffs(efifb.fb_mask_green) - 1; + boff = ffs(efifb.fb_mask_blue) - 1; + + (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB, + efifb.fb_mask_red >> roff, roff, + efifb.fb_mask_green >> goff, goff, + efifb.fb_mask_blue >> boff, boff); + gfx_state.tg_fb.fb_bpp = fls( + efifb.fb_mask_red | efifb.fb_mask_green | + efifb.fb_mask_blue | efifb.fb_mask_reserved); + } + } else { + gfx_state.tg_fb_type = FB_TEXT; + } status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); if (EFI_ERROR(status) || cols * rows == 0) { - cols = 80; - rows = 24; + cols = TEXT_COLS; + rows = TEXT_ROWS; } /* @@ -853,20 +951,74 @@ efi_cons_update_mode(void) */ mode = parse_uefi_con_out(); if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) { - if (buffer != NULL) { - if (tp.tp_row == rows && tp.tp_col == cols) - return (true); - free(buffer); + conout->EnableCursor(conout, FALSE); + gfx_state.tg_cursor_visible = false; + + if (gfx_state.tg_fb_type == FB_TEXT) { + + gfx_state.tg_functions = &tf; + /* ensure the following are not set for text mode */ + unsetenv("screen.height"); + unsetenv("screen.width"); + unsetenv("screen.depth"); } else { - teken_init(&teken, &tf, NULL); + uint32_t fb_height, fb_width; + + fb_height = gfx_state.tg_fb.fb_height; + fb_width = gfx_state.tg_fb.fb_width; + + /* + * setup_font() can adjust terminal size. + * Note, we assume 80x24 terminal first, this is + * because the font selection will attempt to achieve + * at least this terminal dimension and we do not + * end up with too small font. + */ + gfx_state.tg_tp.tp_row = TEXT_ROWS; + gfx_state.tg_tp.tp_col = TEXT_COLS; + setup_font(&gfx_state, fb_height, fb_width); + rows = gfx_state.tg_tp.tp_row; + cols = gfx_state.tg_tp.tp_col; + /* Point of origin in pixels. */ + gfx_state.tg_origin.tp_row = (fb_height - + (rows * gfx_state.tg_font.vf_height)) / 2; + gfx_state.tg_origin.tp_col = (fb_width - + (cols * gfx_state.tg_font.vf_width)) / 2; + + /* UEFI gop has depth 32. */ + gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height * + gfx_state.tg_font.vf_width * 4; + free(gfx_state.tg_glyph); + gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size); + if (gfx_state.tg_glyph == NULL) + return (false); + + gfx_state.tg_functions = &tfx; + snprintf(env, sizeof (env), "%d", fb_height); + env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK, + env, env_noset, env_screen_nounset); + snprintf(env, sizeof (env), "%d", fb_width); + env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK, + env, env_noset, env_screen_nounset); + snprintf(env, sizeof (env), "%d", + gfx_state.tg_fb.fb_bpp); + env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK, + env, env_noset, env_screen_nounset); } - tp.tp_row = rows; - tp.tp_col = cols; - buffer = malloc(rows * cols * sizeof(*buffer)); - if (buffer != NULL) { - teken_set_winsize(&teken, &tp); - a = teken_get_defattr(&teken); + /* Record our terminal screen size. */ + gfx_state.tg_tp.tp_row = rows; + gfx_state.tg_tp.tp_col = cols; + + teken_init(&gfx_state.tg_teken, gfx_state.tg_functions, + &gfx_state); + + free(screen_buffer); + screen_buffer = malloc(rows * cols * sizeof(*screen_buffer)); + if (screen_buffer != NULL) { + teken_set_winsize(&gfx_state.tg_teken, + &gfx_state.tg_tp); + a = teken_get_defattr(&gfx_state.tg_teken); attr = *a; /* @@ -880,7 +1032,7 @@ efi_cons_update_mode(void) ptr = getenv("teken.bg_color"); attr.ta_bgcolor = strtol(ptr, NULL, 10); - teken_set_defattr(&teken, &attr); + teken_set_defattr(&gfx_state.tg_teken, &attr); } else { snprintf(env, sizeof(env), "%d", attr.ta_fgcolor); @@ -891,18 +1043,12 @@ efi_cons_update_mode(void) env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors, env_nounset); } - - for (int row = 0; row < rows; row++) { - for (int col = 0; col < cols; col++) { - buffer[col + row * tp.tp_col].c = ' '; - buffer[col + row * tp.tp_col].a = attr; - } - } } } + if (screen_buffer == NULL) { + conout->EnableCursor(conout, TRUE); #ifdef TERM_EMU - if (buffer == NULL) { conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, DEFAULT_BGCOLOR)); end_term(); @@ -910,8 +1056,23 @@ efi_cons_update_mode(void) curs_move(&curx, &cury, curx, cury); fg_c = DEFAULT_FGCOLOR; bg_c = DEFAULT_BGCOLOR; - } #endif + } else { + /* Improve visibility */ + if (attr.ta_bgcolor == TC_WHITE) + attr.ta_bgcolor |= TC_LIGHT; + teken_set_defattr(&gfx_state.tg_teken, &attr); + + /* Draw frame around terminal area. */ + cons_draw_frame(&attr); + /* + * Erase display, this will also fill our screen + * buffer. + */ + teken_input(&gfx_state.tg_teken, "\e[2J", 4); + gfx_state.tg_functions->tf_param(&gfx_state, + TP_SHOWCURSOR, 1); + } snprintf(env, sizeof (env), "%u", (unsigned)rows); setenv("LINES", env, 1); @@ -926,8 +1087,13 @@ efi_cons_init(int arg) { EFI_STATUS status; - conout->EnableCursor(conout, TRUE); - if (efi_cons_update_mode()) + if (efi_started) + return (0); + + efi_started = true; + + gfx_framework_init(); + if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT)) return (0); return (1); @@ -1035,12 +1201,12 @@ efi_cons_putchar(int c) * Don't use Teken when we're doing pure serial, or a multiple console * with video "primary" because that's also serial. */ - if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || buffer == NULL) { + if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) { input_byte(ch); return; } - teken_input(&teken, &ch, sizeof (ch)); + teken_input(&gfx_state.tg_teken, &ch, sizeof (ch)); } static int |