2 * Copyright (c) 2000 Doug Rabson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
34 #include <sys/reboot.h>
35 #include <machine/metadata.h>
37 #include <framebuffer.h>
38 #include "bootstrap.h"
40 extern EFI_GUID gop_guid;
42 bool boot_services_active = true; /* boot services active first thing in main */
44 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
45 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
46 static SIMPLE_INPUT_INTERFACE *conin;
47 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
48 static bool efi_started;
49 static int mode; /* Does ConOut have serial console? */
51 static uint32_t utf8_left;
52 static uint32_t utf8_partial;
54 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY
55 #define DEFAULT_BGCOLOR EFI_BLACK
58 static int args[MAXARGS], argc;
59 static int fg_c, bg_c, curx, cury;
62 void get_pos(int *x, int *y);
63 void curs_move(int *_x, int *_y, int x, int y);
72 static tf_bell_t efi_cons_bell;
73 static tf_cursor_t efi_text_cursor;
74 static tf_putchar_t efi_text_putchar;
75 static tf_fill_t efi_text_fill;
76 static tf_copy_t efi_text_copy;
77 static tf_param_t efi_text_param;
78 static tf_respond_t efi_cons_respond;
80 static teken_funcs_t tf = {
81 .tf_bell = efi_cons_bell,
82 .tf_cursor = efi_text_cursor,
83 .tf_putchar = efi_text_putchar,
84 .tf_fill = efi_text_fill,
85 .tf_copy = efi_text_copy,
86 .tf_param = efi_text_param,
87 .tf_respond = efi_cons_respond,
90 static teken_funcs_t tfx = {
91 .tf_bell = efi_cons_bell,
92 .tf_cursor = gfx_fb_cursor,
93 .tf_putchar = gfx_fb_putchar,
94 .tf_fill = gfx_fb_fill,
95 .tf_copy = gfx_fb_copy,
96 .tf_param = gfx_fb_param,
97 .tf_respond = efi_cons_respond,
101 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */
102 static int key_pending;
104 static const unsigned char teken_color_to_efi_color[16] = {
123 static void efi_cons_probe(struct console *);
124 static int efi_cons_init(int);
125 void efi_cons_putchar(int);
126 int efi_cons_getchar(void);
127 void efi_cons_efiputchar(int);
128 int efi_cons_poll(void);
129 static void cons_draw_frame(teken_attr_t *);
131 struct console efi_console = {
143 * This function is used to mark a rectangular image area so the scrolling
144 * will know we need to copy the data from there.
147 term_image_display(teken_gfx_t *state, const teken_rect_t *r)
152 if (screen_buffer == NULL)
155 for (p.tp_row = r->tr_begin.tp_row;
156 p.tp_row < r->tr_end.tp_row; p.tp_row++) {
157 for (p.tp_col = r->tr_begin.tp_col;
158 p.tp_col < r->tr_end.tp_col; p.tp_col++) {
159 idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
160 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
162 screen_buffer[idx].a.ta_format |= TF_IMAGE;
171 efi_cons_bell(void *s __unused)
176 efi_text_cursor(void *arg, const teken_pos_t *p)
178 teken_gfx_t *state = arg;
181 if (!boot_services_active)
185 if (p->tp_row >= state->tg_tp.tp_row)
186 row = state->tg_tp.tp_row - 1;
189 if (p->tp_col >= state->tg_tp.tp_col)
190 col = state->tg_tp.tp_col - 1;
192 conout->SetCursorPosition(conout, col, row);
196 efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll)
199 struct text_pixel *px;
200 teken_color_t fg, bg, tmp;
202 px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col;
203 a = conout->Mode->Attribute;
205 fg = teken_256to16(px->a.ta_fgcolor);
206 bg = teken_256to16(px->a.ta_bgcolor);
207 if (px->a.ta_format & TF_BOLD)
209 if (px->a.ta_format & TF_BLINK)
212 if (px->a.ta_format & TF_REVERSE) {
218 attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg],
219 teken_color_to_efi_color[bg] & 0x7);
221 conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
223 /* to prevent autoscroll, skip print of lower right char */
225 p->tp_row == state->tg_tp.tp_row - 1 &&
226 p->tp_col == state->tg_tp.tp_col - 1)
229 (void) conout->SetAttribute(conout, attr);
230 efi_cons_efiputchar(px->c);
231 (void) conout->SetAttribute(conout, a);
235 efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c,
236 const teken_attr_t *a)
238 teken_gfx_t *state = s;
242 if (!boot_services_active)
245 idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
246 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
249 screen_buffer[idx].c = c;
250 screen_buffer[idx].a = *a;
252 efi_text_printchar(s, p, false);
256 efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c,
257 const teken_attr_t *a)
259 teken_gfx_t *state = arg;
262 if (!boot_services_active)
265 if (state->tg_cursor_visible)
266 conout->EnableCursor(conout, FALSE);
267 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
269 for (p.tp_col = r->tr_begin.tp_col;
270 p.tp_col < r->tr_end.tp_col; p.tp_col++)
271 efi_text_putchar(state, &p, c, a);
272 if (state->tg_cursor_visible)
273 conout->EnableCursor(conout, TRUE);
277 efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s,
278 teken_pos_t *d, bool scroll)
280 unsigned soffset, doffset;
284 soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
285 doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
289 for (x = 0; x < ncol; x++) {
290 sp.tp_col = s->tp_col + x;
291 dp.tp_col = d->tp_col + x;
292 if (!is_same_pixel(&screen_buffer[soffset + x],
293 &screen_buffer[doffset + x])) {
294 screen_buffer[doffset + x] =
295 screen_buffer[soffset + x];
297 efi_text_printchar(state, &dp, false);
299 /* Draw last char and trigger scroll. */
300 if (dp.tp_col + 1 == state->tg_tp.tp_col &&
301 dp.tp_row + 1 == state->tg_tp.tp_row) {
302 efi_text_printchar(state, &dp, true);
309 efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
311 teken_gfx_t *state = arg;
312 unsigned doffset, soffset;
314 int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
317 if (!boot_services_active)
321 * Copying is a little tricky. We must make sure we do it in
322 * correct order, to make sure we don't overwrite our own data.
325 nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
326 ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
329 * Check if we do copy whole screen.
331 if (p->tp_row == 0 && p->tp_col == 0 &&
332 nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2)
335 soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
336 doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
338 /* remove the cursor */
339 if (state->tg_cursor_visible)
340 conout->EnableCursor(conout, FALSE);
345 if (doffset <= soffset) {
348 for (y = 0; y < nrow; y++) {
349 s.tp_row = r->tr_begin.tp_row + y;
350 d.tp_row = p->tp_row + y;
352 efi_text_copy_line(state, ncol, &s, &d, scroll);
355 for (y = nrow - 1; y >= 0; y--) {
356 s.tp_row = r->tr_begin.tp_row + y;
357 d.tp_row = p->tp_row + y;
359 efi_text_copy_line(state, ncol, &s, &d, false);
363 /* display the cursor */
364 if (state->tg_cursor_visible)
365 conout->EnableCursor(conout, TRUE);
369 efi_text_param(void *arg, int cmd, unsigned int value)
371 teken_gfx_t *state = arg;
373 if (!boot_services_active)
377 case TP_SETLOCALCURSOR:
379 * 0 means normal (usually block), 1 means hidden, and
380 * 2 means blinking (always block) for compatibility with
381 * syscons. We don't support any changes except hiding,
382 * so must map 2 to 0.
384 value = (value == 1) ? 0 : 1;
388 conout->EnableCursor(conout, TRUE);
389 state->tg_cursor_visible = true;
391 conout->EnableCursor(conout, FALSE);
392 state->tg_cursor_visible = false;
396 /* Not yet implemented */
405 efi_cons_respond(void *s __unused, const void *buf __unused,
411 * Set up conin/conout/coninex to make sure we have input ready.
414 efi_cons_probe(struct console *cp)
422 * Call SetMode to work around buggy firmware.
424 status = conout->SetMode(conout, conout->Mode->Mode);
426 if (coninex == NULL) {
427 status = BS->OpenProtocol(ST->ConsoleInHandle,
428 &simple_input_ex_guid, (void **)&coninex,
429 IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
430 if (status != EFI_SUCCESS)
434 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
438 color_name_to_teken(const char *name, int *val)
441 if (strncasecmp(name, "light", 5) == 0) {
444 } else if (strncasecmp(name, "bright", 6) == 0) {
448 if (strcasecmp(name, "black") == 0) {
449 *val = TC_BLACK | light;
452 if (strcasecmp(name, "red") == 0) {
453 *val = TC_RED | light;
456 if (strcasecmp(name, "green") == 0) {
457 *val = TC_GREEN | light;
460 if (strcasecmp(name, "yellow") == 0 || strcasecmp(name, "brown") == 0) {
461 *val = TC_YELLOW | light;
464 if (strcasecmp(name, "blue") == 0) {
465 *val = TC_BLUE | light;
468 if (strcasecmp(name, "magenta") == 0) {
469 *val = TC_MAGENTA | light;
472 if (strcasecmp(name, "cyan") == 0) {
473 *val = TC_CYAN | light;
476 if (strcasecmp(name, "white") == 0) {
477 *val = TC_WHITE | light;
484 efi_set_colors(struct env_var *ev, int flags, const void *value)
489 const teken_attr_t *ap;
495 if (color_name_to_teken(value, &val)) {
496 snprintf(buf, sizeof (buf), "%d", val);
503 lval = strtol(value, &end, 0);
504 if (errno != 0 || *end != '\0' || lval < 0 || lval > 15) {
505 printf("Allowed values are either ansi color name or "
506 "number from range [0-15].\n");
513 ap = teken_get_defattr(&gfx_state.tg_teken);
515 if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
516 /* is it already set? */
517 if (ap->ta_fgcolor == val)
521 if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
522 /* is it already set? */
523 if (ap->ta_bgcolor == val)
528 /* Improve visibility */
529 if (a.ta_bgcolor == TC_WHITE)
530 a.ta_bgcolor |= TC_LIGHT;
532 teken_set_defattr(&gfx_state.tg_teken, &a);
534 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
535 teken_input(&gfx_state.tg_teken, "\e[2J", 4);
540 /* Get cursor position. */
542 get_pos(int *x, int *y)
544 *x = conout->Mode->CursorColumn;
545 *y = conout->Mode->CursorRow;
548 /* Move cursor to x rows and y cols (0-based). */
550 curs_move(int *_x, int *_y, int x, int y)
552 conout->SetCursorPosition(conout, x, y);
554 *_x = conout->Mode->CursorColumn;
556 *_y = conout->Mode->CursorRow;
559 /* Clear internal state of the terminal emulation code. */
569 efi_cons_rawputchar(int c)
573 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
578 n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
579 for (i = 0; i < n; i++)
580 efi_cons_rawputchar(' ');
584 efi_cons_efiputchar('\r');
585 efi_cons_efiputchar(c);
590 efi_cons_efiputchar('\r');
593 efi_cons_efiputchar('\n');
594 efi_cons_efiputchar('\r');
602 efi_cons_efiputchar('\b');
607 efi_cons_efiputchar(c);
620 conout->EnableCursor(conout, TRUE);
624 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
632 efi_cons_rawputchar('\033');
634 efi_cons_rawputchar(esc);
635 for (i = 0; i <= argc; ++i) {
636 sprintf(buf, "%d", args[i]);
639 efi_cons_rawputchar(*ch++);
642 efi_cons_rawputchar(c);
646 /* Clear display from current position to end of screen. */
653 get_pos(&curx, &cury);
654 if (curx == 0 && cury == 0) {
655 conout->ClearScreen(conout);
660 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
661 CL(0); /* clear current line from cursor to end */
662 for (i = cury + 1; i < y-1; i++) {
663 curs_move(NULL, NULL, 0, i);
666 curs_move(NULL, NULL, curx, cury);
671 * Absolute cursor move to args[0] rows and args[1] columns
672 * (the coordinates are 1-based).
681 curs_move(&curx, &cury, args[1], args[0]);
685 /* Home cursor (left top corner), also called from mode command. */
690 args[0] = args[1] = 1;
694 /* Clear line from current position to end of line */
702 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
704 case 0: /* from cursor to end */
707 case 1: /* from beginning to cursor */
710 case 2: /* entire line */
713 default: /* NOTREACHED */
720 line = malloc(len * sizeof (CHAR16));
722 printf("out of memory\n");
725 for (i = 0; i < len; i++)
730 curs_move(NULL, NULL, 0, cury);
732 conout->OutputString(conout, line);
733 /* restore cursor position */
734 curs_move(NULL, NULL, curx, cury);
745 args[argc] += c - '0';
749 /* Emulate basic capabilities of cons25 terminal */
753 if (!boot_services_active)
756 static int ansi_col[] = {
757 0, 4, 2, 6, 1, 5, 3, 7
769 efi_cons_rawputchar(c);
790 else if (argc + 1 >= MAXARGS)
795 case 'H': /* ho = \E[H */
803 case 'J': /* cd = \E[J */
811 fg_c = DEFAULT_FGCOLOR;
812 bg_c = DEFAULT_BGCOLOR;
814 for (i = 0; i <= argc; ++i) {
816 case 0: /* back to normal */
817 fg_c = DEFAULT_FGCOLOR;
818 bg_c = DEFAULT_BGCOLOR;
823 case 4: /* underline */
827 case 7: /* reverse */
832 case 22: /* normal intensity */
835 case 24: /* not underline */
836 case 25: /* not blinking */
839 case 30: case 31: case 32: case 33:
840 case 34: case 35: case 36: case 37:
841 fg_c = ansi_col[args[i] - 30];
843 case 39: /* normal */
844 fg_c = DEFAULT_FGCOLOR;
846 case 40: case 41: case 42: case 43:
847 case 44: case 45: case 46: case 47:
848 bg_c = ansi_col[args[i] - 40];
850 case 49: /* normal */
851 bg_c = DEFAULT_BGCOLOR;
855 conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
871 efi_cons_rawputchar(c);
876 env_screen_nounset(struct env_var *ev __unused)
878 if (gfx_state.tg_fb_type == FB_TEXT)
884 cons_draw_frame(teken_attr_t *a)
886 teken_attr_t attr = *a;
887 teken_color_t fg = a->ta_fgcolor;
889 attr.ta_fgcolor = attr.ta_bgcolor;
890 teken_set_defattr(&gfx_state.tg_teken, &attr);
892 gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width,
893 gfx_state.tg_origin.tp_row, 1);
895 gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1,
896 gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1);
897 gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row,
898 gfx_state.tg_origin.tp_col,
899 gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1);
901 gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1,
902 gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width,
903 gfx_state.tg_fb.fb_height, 1);
905 attr.ta_fgcolor = fg;
906 teken_set_defattr(&gfx_state.tg_teken, &attr);
910 cons_update_mode(bool use_gfx_mode)
913 const teken_attr_t *a;
922 * Despite the use_gfx_mode, we want to make sure we call
923 * efi_find_framebuffer(). This will populate the fb data,
924 * which will be passed to kernel.
926 if (efi_find_framebuffer(&gfx_state) == 0 && use_gfx_mode) {
927 int roff, goff, boff;
929 roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
930 goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
931 boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
933 (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB,
934 gfx_state.tg_fb.fb_mask_red >> roff, roff,
935 gfx_state.tg_fb.fb_mask_green >> goff, goff,
936 gfx_state.tg_fb.fb_mask_blue >> boff, boff);
939 * Either text mode was asked by user or we failed to
942 gfx_state.tg_fb_type = FB_TEXT;
945 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
946 if (EFI_ERROR(status) || cols * rows == 0) {
952 * When we have serial port listed in ConOut, use pre-teken emulator,
954 * The problem is, we can not output text on efi and comconsole when
955 * efi also has comconsole bound. But then again, we need to have
956 * terminal emulator for efi text mode to support the menu.
957 * While teken is too expensive to be used on serial console, the
958 * pre-teken emulator is light enough to be used on serial console.
960 * When doing multiple consoles (both serial and video),
961 * also just use the old emulator. RB_MULTIPLE also implies
962 * we're using a serial console.
964 mode = parse_uefi_con_out();
965 if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
966 conout->EnableCursor(conout, FALSE);
967 gfx_state.tg_cursor_visible = false;
969 if (gfx_state.tg_fb_type == FB_TEXT) {
971 gfx_state.tg_functions = &tf;
972 /* ensure the following are not set for text mode */
973 unsetenv("screen.height");
974 unsetenv("screen.width");
975 unsetenv("screen.depth");
977 uint32_t fb_height, fb_width;
979 fb_height = gfx_state.tg_fb.fb_height;
980 fb_width = gfx_state.tg_fb.fb_width;
983 * setup_font() can adjust terminal size.
984 * We can see two kind of bad happening.
985 * We either can get too small console font - requested
986 * terminal size is large, display resolution is
987 * large, and we get very small font.
988 * Or, we can get too large font - requested
989 * terminal size is small and this will cause large
990 * font to be selected.
991 * Now, the setup_font() is updated to consider
992 * display density and this should give us mostly
993 * acceptable font. However, the catch is, not all
994 * display devices will give us display density.
995 * Still, we do hope, external monitors do - this is
996 * where the display size will matter the most.
997 * And for laptop screens, we should still get good
998 * results by requesting 80x25 terminal.
1000 gfx_state.tg_tp.tp_row = 25;
1001 gfx_state.tg_tp.tp_col = 80;
1002 setup_font(&gfx_state, fb_height, fb_width);
1003 rows = gfx_state.tg_tp.tp_row;
1004 cols = gfx_state.tg_tp.tp_col;
1005 /* Point of origin in pixels. */
1006 gfx_state.tg_origin.tp_row = (fb_height -
1007 (rows * gfx_state.tg_font.vf_height)) / 2;
1008 gfx_state.tg_origin.tp_col = (fb_width -
1009 (cols * gfx_state.tg_font.vf_width)) / 2;
1011 /* UEFI gop has depth 32. */
1012 gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height *
1013 gfx_state.tg_font.vf_width * 4;
1014 free(gfx_state.tg_glyph);
1015 gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size);
1016 if (gfx_state.tg_glyph == NULL)
1019 gfx_state.tg_functions = &tfx;
1020 snprintf(env, sizeof (env), "%d", fb_height);
1021 env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK,
1022 env, env_noset, env_screen_nounset);
1023 snprintf(env, sizeof (env), "%d", fb_width);
1024 env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK,
1025 env, env_noset, env_screen_nounset);
1026 snprintf(env, sizeof (env), "%d",
1027 gfx_state.tg_fb.fb_bpp);
1028 env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK,
1029 env, env_noset, env_screen_nounset);
1032 /* Record our terminal screen size. */
1033 gfx_state.tg_tp.tp_row = rows;
1034 gfx_state.tg_tp.tp_col = cols;
1036 teken_init(&gfx_state.tg_teken, gfx_state.tg_functions,
1039 free(screen_buffer);
1040 screen_buffer = malloc(rows * cols * sizeof(*screen_buffer));
1041 if (screen_buffer != NULL) {
1042 teken_set_winsize(&gfx_state.tg_teken,
1044 a = teken_get_defattr(&gfx_state.tg_teken);
1048 * On first run, we set up the efi_set_colors()
1049 * callback. If the env is already set, we
1050 * pick up fg and bg color values from the environment.
1052 ptr = getenv("teken.fg_color");
1054 attr.ta_fgcolor = strtol(ptr, NULL, 10);
1055 ptr = getenv("teken.bg_color");
1056 attr.ta_bgcolor = strtol(ptr, NULL, 10);
1058 teken_set_defattr(&gfx_state.tg_teken, &attr);
1060 snprintf(env, sizeof(env), "%d",
1062 env_setenv("teken.fg_color", EV_VOLATILE, env,
1063 efi_set_colors, env_nounset);
1064 snprintf(env, sizeof(env), "%d",
1066 env_setenv("teken.bg_color", EV_VOLATILE, env,
1067 efi_set_colors, env_nounset);
1072 if (screen_buffer == NULL) {
1073 conout->EnableCursor(conout, TRUE);
1075 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
1078 get_pos(&curx, &cury);
1079 curs_move(&curx, &cury, curx, cury);
1080 fg_c = DEFAULT_FGCOLOR;
1081 bg_c = DEFAULT_BGCOLOR;
1084 /* Improve visibility */
1085 if (attr.ta_bgcolor == TC_WHITE)
1086 attr.ta_bgcolor |= TC_LIGHT;
1087 teken_set_defattr(&gfx_state.tg_teken, &attr);
1089 /* Draw frame around terminal area. */
1090 cons_draw_frame(&attr);
1092 * Erase display, this will also fill our screen
1095 teken_input(&gfx_state.tg_teken, "\e[2J", 4);
1096 gfx_state.tg_functions->tf_param(&gfx_state,
1100 snprintf(env, sizeof (env), "%u", (unsigned)rows);
1101 setenv("LINES", env, 1);
1102 snprintf(env, sizeof (env), "%u", (unsigned)cols);
1103 setenv("COLUMNS", env, 1);
1109 efi_cons_init(int arg)
1118 gfx_framework_init();
1119 if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT))
1134 for (i = 0; i < sizeof(utf8_partial); i++) {
1135 c = (utf8_partial >> (24 - (i << 3))) & 0xff;
1144 input_byte(uint8_t c)
1146 if ((c & 0x80) == 0x00) {
1147 /* One-byte sequence. */
1152 if ((c & 0xe0) == 0xc0) {
1153 /* Two-byte sequence. */
1159 if ((c & 0xf0) == 0xe0) {
1160 /* Three-byte sequence. */
1166 if ((c & 0xf8) == 0xf0) {
1167 /* Four-byte sequence. */
1173 if ((c & 0xc0) == 0x80) {
1174 /* Invalid state? */
1175 if (utf8_left == 0) {
1180 utf8_partial = (utf8_partial << 8) | c;
1181 if (utf8_left == 0) {
1187 b = (u >> 24) & 0xff;
1188 if (b != 0) { /* Four-byte sequence */
1190 b = (u >> 16) & 0xff;
1191 v = (v << 6) | (b & 0x3f);
1192 b = (u >> 8) & 0xff;
1193 v = (v << 6) | (b & 0x3f);
1195 v = (v << 6) | (b & 0x3f);
1196 } else if ((b = (u >> 16) & 0xff) != 0) {
1197 v = b & 0x0f; /* Three-byte sequence */
1198 b = (u >> 8) & 0xff;
1199 v = (v << 6) | (b & 0x3f);
1201 v = (v << 6) | (b & 0x3f);
1202 } else if ((b = (u >> 8) & 0xff) != 0) {
1203 v = b & 0x1f; /* Two-byte sequence */
1205 v = (v << 6) | (b & 0x3f);
1207 /* Send unicode char directly to console. */
1208 efi_cons_efiputchar(v);
1213 /* Anything left is illegal in UTF-8 sequence. */
1219 efi_cons_putchar(int c)
1221 unsigned char ch = c;
1224 * Don't use Teken when we're doing pure serial, or a multiple console
1225 * with video "primary" because that's also serial.
1227 if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) {
1232 teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
1236 keybuf_getchar(void)
1240 for (i = 0; i < KEYBUFSZ; i++) {
1241 if (keybuf[i] != 0) {
1256 for (i = 0; i < KEYBUFSZ; i++) {
1264 * We are not reading input before keybuf is empty, so we are safe
1265 * just to fill keybuf from the beginning.
1268 keybuf_inschar(EFI_INPUT_KEY *key)
1271 switch (key->ScanCode) {
1272 case SCAN_UP: /* UP */
1273 keybuf[0] = 0x1b; /* esc */
1277 case SCAN_DOWN: /* DOWN */
1278 keybuf[0] = 0x1b; /* esc */
1282 case SCAN_RIGHT: /* RIGHT */
1283 keybuf[0] = 0x1b; /* esc */
1287 case SCAN_LEFT: /* LEFT */
1288 keybuf[0] = 0x1b; /* esc */
1293 keybuf[0] = CHAR_BACKSPACE;
1296 keybuf[0] = 0x1b; /* esc */
1299 keybuf[0] = key->UnicodeChar;
1310 status = conin->ReadKeyStroke(conin, &key);
1311 if (status == EFI_SUCCESS) {
1312 keybuf_inschar(&key);
1319 efi_readkey_ex(void)
1323 EFI_KEY_DATA key_data;
1326 status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1327 if (status == EFI_SUCCESS) {
1328 kss = key_data.KeyState.KeyShiftState;
1330 if (kss & EFI_SHIFT_STATE_VALID) {
1333 * quick mapping to control chars, replace with
1336 if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1337 kss & EFI_LEFT_CONTROL_PRESSED) {
1338 if (kp->UnicodeChar >= 'a' &&
1339 kp->UnicodeChar <= 'z') {
1340 kp->UnicodeChar -= 'a';
1346 * The shift state and/or toggle state may not be valid,
1347 * but we still can have ScanCode or UnicodeChar.
1349 if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1358 efi_cons_getchar(void)
1362 if ((c = keybuf_getchar()) != 0)
1367 if (coninex == NULL) {
1369 return (keybuf_getchar());
1371 if (efi_readkey_ex())
1372 return (keybuf_getchar());
1383 if (keybuf_ischar() || key_pending)
1387 * Some EFI implementation (u-boot for example) do not support
1389 * CheckEvent() can clear the signaled state.
1391 if (coninex != NULL) {
1392 if (coninex->WaitForKeyEx == NULL) {
1393 key_pending = efi_readkey_ex();
1395 status = BS->CheckEvent(coninex->WaitForKeyEx);
1396 key_pending = status == EFI_SUCCESS;
1399 if (conin->WaitForKey == NULL) {
1400 key_pending = efi_readkey();
1402 status = BS->CheckEvent(conin->WaitForKey);
1403 key_pending = status == EFI_SUCCESS;
1407 return (key_pending);
1410 /* Plain direct access to EFI OutputString(). */
1412 efi_cons_efiputchar(int c)
1418 buf[1] = 0; /* terminate string */
1420 status = conout->TestString(conout, buf);
1421 if (EFI_ERROR(status))
1423 conout->OutputString(conout, buf);