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$");
33 #include <sys/reboot.h>
35 #include "bootstrap.h"
37 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
38 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
39 static SIMPLE_INPUT_INTERFACE *conin;
40 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
42 static int mode; /* Does ConOut have serial console? */
44 static uint32_t utf8_left;
45 static uint32_t utf8_partial;
47 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY
48 #define DEFAULT_BGCOLOR EFI_BLACK
51 static int args[MAXARGS], argc;
52 static int fg_c, bg_c, curx, cury;
55 void get_pos(int *x, int *y);
56 void curs_move(int *_x, int *_y, int x, int y);
62 static tf_bell_t efi_cons_bell;
63 static tf_cursor_t efi_text_cursor;
64 static tf_putchar_t efi_text_putchar;
65 static tf_fill_t efi_text_fill;
66 static tf_copy_t efi_text_copy;
67 static tf_param_t efi_text_param;
68 static tf_respond_t efi_cons_respond;
70 static teken_funcs_t tf = {
71 .tf_bell = efi_cons_bell,
72 .tf_cursor = efi_text_cursor,
73 .tf_putchar = efi_text_putchar,
74 .tf_fill = efi_text_fill,
75 .tf_copy = efi_text_copy,
76 .tf_param = efi_text_param,
77 .tf_respond = efi_cons_respond,
88 static struct text_pixel *buffer;
91 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */
92 static int key_pending;
94 static const unsigned char teken_color_to_efi_color[16] = {
113 static void efi_cons_probe(struct console *);
114 static int efi_cons_init(int);
115 void efi_cons_putchar(int);
116 int efi_cons_getchar(void);
117 void efi_cons_efiputchar(int);
118 int efi_cons_poll(void);
120 struct console efi_console = {
135 efi_cons_bell(void *s __unused)
140 efi_text_cursor(void *s __unused, const teken_pos_t *p)
144 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
146 if (p->tp_col == col)
151 if (p->tp_row == row)
156 conout->SetCursorPosition(conout, col, row);
160 efi_text_printchar(const teken_pos_t *p, bool autoscroll)
163 struct text_pixel *px;
164 teken_color_t fg, bg, tmp;
166 px = buffer + p->tp_col + p->tp_row * tp.tp_col;
167 a = conout->Mode->Attribute;
169 fg = teken_256to16(px->a.ta_fgcolor);
170 bg = teken_256to16(px->a.ta_bgcolor);
171 if (px->a.ta_format & TF_BOLD)
173 if (px->a.ta_format & TF_BLINK)
176 if (px->a.ta_format & TF_REVERSE) {
182 attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg],
183 teken_color_to_efi_color[bg] & 0x7);
185 conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
187 /* to prvent autoscroll, skip print of lower right char */
189 p->tp_row == tp.tp_row - 1 &&
190 p->tp_col == tp.tp_col - 1)
193 (void) conout->SetAttribute(conout, attr);
194 efi_cons_efiputchar(px->c);
195 (void) conout->SetAttribute(conout, a);
199 efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c,
200 const teken_attr_t *a)
205 idx = p->tp_col + p->tp_row * tp.tp_col;
208 efi_text_printchar(p, false);
212 efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
213 const teken_attr_t *a)
218 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
220 conout->EnableCursor(conout, FALSE);
221 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
223 for (p.tp_col = r->tr_begin.tp_col;
224 p.tp_col < r->tr_end.tp_col; p.tp_col++)
225 efi_text_putchar(s, &p, c, a);
226 conout->EnableCursor(conout, TRUE);
230 efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
232 if (px1->c != px2->c)
235 if (px1->a.ta_format != px2->a.ta_format)
237 if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
239 if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
246 efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
249 int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
254 * Copying is a little tricky. We must make sure we do it in
255 * correct order, to make sure we don't overwrite our own data.
258 nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
259 ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
262 * Check if we do copy whole screen.
264 if (p->tp_row == 0 && p->tp_col == 0 &&
265 nrow == tp.tp_row - 2 && ncol == tp.tp_col - 2)
268 conout->EnableCursor(conout, FALSE);
269 if (p->tp_row < r->tr_begin.tp_row) {
270 /* Copy from bottom to top. */
271 for (y = 0; y < nrow; y++) {
272 d.tp_row = p->tp_row + y;
273 s.tp_row = r->tr_begin.tp_row + y;
274 drow = d.tp_row * tp.tp_col;
275 srow = s.tp_row * tp.tp_col;
276 for (x = 0; x < ncol; x++) {
277 d.tp_col = p->tp_col + x;
278 s.tp_col = r->tr_begin.tp_col + x;
281 &buffer[d.tp_col + drow],
282 &buffer[s.tp_col + srow])) {
283 buffer[d.tp_col + drow] =
284 buffer[s.tp_col + srow];
286 efi_text_printchar(&d, false);
289 * Draw last char and trigger
294 efi_text_printchar(&d, true);
300 /* Copy from top to bottom. */
301 if (p->tp_col < r->tr_begin.tp_col) {
302 /* Copy from right to left. */
303 for (y = nrow - 1; y >= 0; y--) {
304 d.tp_row = p->tp_row + y;
305 s.tp_row = r->tr_begin.tp_row + y;
306 drow = d.tp_row * tp.tp_col;
307 srow = s.tp_row * tp.tp_col;
308 for (x = 0; x < ncol; x++) {
309 d.tp_col = p->tp_col + x;
310 s.tp_col = r->tr_begin.tp_col + x;
313 &buffer[d.tp_col + drow],
314 &buffer[s.tp_col + srow])) {
315 buffer[d.tp_col + drow] =
316 buffer[s.tp_col + srow];
317 efi_text_printchar(&d, false);
322 /* Copy from left to right. */
323 for (y = nrow - 1; y >= 0; y--) {
324 d.tp_row = p->tp_row + y;
325 s.tp_row = r->tr_begin.tp_row + y;
326 drow = d.tp_row * tp.tp_col;
327 srow = s.tp_row * tp.tp_col;
328 for (x = ncol - 1; x >= 0; x--) {
329 d.tp_col = p->tp_col + x;
330 s.tp_col = r->tr_begin.tp_col + x;
333 &buffer[d.tp_col + drow],
334 &buffer[s.tp_col + srow])) {
335 buffer[d.tp_col + drow] =
336 buffer[s.tp_col + srow];
337 efi_text_printchar(&d, false);
343 conout->EnableCursor(conout, TRUE);
347 efi_text_param(void *s __unused, int cmd, unsigned int value)
350 case TP_SETLOCALCURSOR:
352 * 0 means normal (usually block), 1 means hidden, and
353 * 2 means blinking (always block) for compatibility with
354 * syscons. We don't support any changes except hiding,
355 * so must map 2 to 0.
357 value = (value == 1) ? 0 : 1;
361 conout->EnableCursor(conout, TRUE);
363 conout->EnableCursor(conout, FALSE);
366 /* Not yet implemented */
375 efi_cons_respond(void *s __unused, const void *buf __unused,
381 * Set up conin/conout/coninex to make sure we have input ready.
384 efi_cons_probe(struct console *cp)
392 * Call SetMode to work around buggy firmware.
394 status = conout->SetMode(conout, conout->Mode->Mode);
396 if (coninex == NULL) {
397 status = BS->OpenProtocol(ST->ConsoleInHandle,
398 &simple_input_ex_guid, (void **)&coninex,
399 IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
400 if (status != EFI_SUCCESS)
404 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
408 color_name_to_teken(const char *name, int *val)
410 if (strcasecmp(name, "black") == 0) {
414 if (strcasecmp(name, "red") == 0) {
418 if (strcasecmp(name, "green") == 0) {
422 if (strcasecmp(name, "brown") == 0) {
426 if (strcasecmp(name, "blue") == 0) {
430 if (strcasecmp(name, "magenta") == 0) {
434 if (strcasecmp(name, "cyan") == 0) {
438 if (strcasecmp(name, "white") == 0) {
446 efi_set_colors(struct env_var *ev, int flags, const void *value)
451 const teken_attr_t *ap;
457 if (color_name_to_teken(value, &val)) {
458 snprintf(buf, sizeof (buf), "%d", val);
464 val = (int)strtol(value, &end, 0);
465 if (errno != 0 || *end != '\0') {
466 printf("Allowed values are either ansi color name or "
467 "number from range [0-7].\n");
473 ap = teken_get_defattr(&teken);
475 if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
476 /* is it already set? */
477 if (ap->ta_fgcolor == val)
481 if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
482 /* is it already set? */
483 if (ap->ta_bgcolor == val)
487 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
488 teken_set_defattr(&teken, &a);
493 /* Get cursor position. */
495 get_pos(int *x, int *y)
497 *x = conout->Mode->CursorColumn;
498 *y = conout->Mode->CursorRow;
501 /* Move cursor to x rows and y cols (0-based). */
503 curs_move(int *_x, int *_y, int x, int y)
505 conout->SetCursorPosition(conout, x, y);
507 *_x = conout->Mode->CursorColumn;
509 *_y = conout->Mode->CursorRow;
512 /* Clear internal state of the terminal emulation code. */
522 efi_cons_rawputchar(int c)
526 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
531 n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
532 for (i = 0; i < n; i++)
533 efi_cons_rawputchar(' ');
537 efi_cons_efiputchar('\r');
538 efi_cons_efiputchar(c);
543 efi_cons_efiputchar('\r');
546 efi_cons_efiputchar('\n');
547 efi_cons_efiputchar('\r');
555 efi_cons_efiputchar('\b');
560 efi_cons_efiputchar(c);
573 conout->EnableCursor(conout, TRUE);
577 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
585 efi_cons_rawputchar('\033');
587 efi_cons_rawputchar(esc);
588 for (i = 0; i <= argc; ++i) {
589 sprintf(buf, "%d", args[i]);
592 efi_cons_rawputchar(*ch++);
595 efi_cons_rawputchar(c);
599 /* Clear display from current position to end of screen. */
606 get_pos(&curx, &cury);
607 if (curx == 0 && cury == 0) {
608 conout->ClearScreen(conout);
613 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
614 CL(0); /* clear current line from cursor to end */
615 for (i = cury + 1; i < y-1; i++) {
616 curs_move(NULL, NULL, 0, i);
619 curs_move(NULL, NULL, curx, cury);
624 * Absolute cursor move to args[0] rows and args[1] columns
625 * (the coordinates are 1-based).
634 curs_move(&curx, &cury, args[1], args[0]);
638 /* Home cursor (left top corner), also called from mode command. */
643 args[0] = args[1] = 1;
647 /* Clear line from current position to end of line */
655 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
657 case 0: /* from cursor to end */
660 case 1: /* from beginning to cursor */
663 case 2: /* entire line */
666 default: /* NOTREACHED */
673 line = malloc(len * sizeof (CHAR16));
675 printf("out of memory\n");
678 for (i = 0; i < len; i++)
683 curs_move(NULL, NULL, 0, cury);
685 conout->OutputString(conout, line);
686 /* restore cursor position */
687 curs_move(NULL, NULL, curx, cury);
698 args[argc] += c - '0';
702 /* Emulate basic capabilities of cons25 terminal */
707 static int ansi_col[] = {
708 0, 4, 2, 6, 1, 5, 3, 7
720 efi_cons_rawputchar(c);
741 else if (argc + 1 >= MAXARGS)
746 case 'H': /* ho = \E[H */
754 case 'J': /* cd = \E[J */
762 fg_c = DEFAULT_FGCOLOR;
763 bg_c = DEFAULT_BGCOLOR;
765 for (i = 0; i <= argc; ++i) {
767 case 0: /* back to normal */
768 fg_c = DEFAULT_FGCOLOR;
769 bg_c = DEFAULT_BGCOLOR;
774 case 4: /* underline */
778 case 7: /* reverse */
783 case 22: /* normal intensity */
786 case 24: /* not underline */
787 case 25: /* not blinking */
790 case 30: case 31: case 32: case 33:
791 case 34: case 35: case 36: case 37:
792 fg_c = ansi_col[args[i] - 30];
794 case 39: /* normal */
795 fg_c = DEFAULT_FGCOLOR;
797 case 40: case 41: case 42: case 43:
798 case 44: case 45: case 46: case 47:
799 bg_c = ansi_col[args[i] - 40];
801 case 49: /* normal */
802 bg_c = DEFAULT_BGCOLOR;
806 conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
822 efi_cons_rawputchar(c);
827 efi_cons_update_mode(void)
830 const teken_attr_t *a;
835 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
836 if (EFI_ERROR(status) || cols * rows == 0) {
842 * When we have serial port listed in ConOut, use pre-teken emulator,
844 * The problem is, we can not output text on efi and comconsole when
845 * efi also has comconsole bound. But then again, we need to have
846 * terminal emulator for efi text mode to support the menu.
847 * While teken is too expensive to be used on serial console, the
848 * pre-teken emulator is light enough to be used on serial console.
850 * When doing multiple consoles (both serial and video),
851 * also just use the old emulator. RB_MULTIPLE also implies
852 * we're using a serial console.
854 mode = parse_uefi_con_out();
855 if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
856 if (buffer != NULL) {
857 if (tp.tp_row == rows && tp.tp_col == cols)
861 teken_init(&teken, &tf, NULL);
866 buffer = malloc(rows * cols * sizeof(*buffer));
867 if (buffer != NULL) {
868 teken_set_winsize(&teken, &tp);
869 a = teken_get_defattr(&teken);
873 * On first run, we set up the efi_set_colors()
874 * callback. If the env is already set, we
875 * pick up fg and bg color values from the environment.
877 ptr = getenv("teken.fg_color");
879 attr.ta_fgcolor = strtol(ptr, NULL, 10);
880 ptr = getenv("teken.bg_color");
881 attr.ta_bgcolor = strtol(ptr, NULL, 10);
883 teken_set_defattr(&teken, &attr);
885 snprintf(env, sizeof(env), "%d",
887 env_setenv("teken.fg_color", EV_VOLATILE, env,
888 efi_set_colors, env_nounset);
889 snprintf(env, sizeof(env), "%d",
891 env_setenv("teken.bg_color", EV_VOLATILE, env,
892 efi_set_colors, env_nounset);
895 for (int row = 0; row < rows; row++) {
896 for (int col = 0; col < cols; col++) {
897 buffer[col + row * tp.tp_col].c = ' ';
898 buffer[col + row * tp.tp_col].a = attr;
905 if (buffer == NULL) {
906 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
909 get_pos(&curx, &cury);
910 curs_move(&curx, &cury, curx, cury);
911 fg_c = DEFAULT_FGCOLOR;
912 bg_c = DEFAULT_BGCOLOR;
916 snprintf(env, sizeof (env), "%u", (unsigned)rows);
917 setenv("LINES", env, 1);
918 snprintf(env, sizeof (env), "%u", (unsigned)cols);
919 setenv("COLUMNS", env, 1);
925 efi_cons_init(int arg)
929 conout->EnableCursor(conout, TRUE);
930 if (efi_cons_update_mode())
945 for (i = 0; i < sizeof(utf8_partial); i++) {
946 c = (utf8_partial >> (24 - (i << 3))) & 0xff;
955 input_byte(uint8_t c)
957 if ((c & 0x80) == 0x00) {
958 /* One-byte sequence. */
963 if ((c & 0xe0) == 0xc0) {
964 /* Two-byte sequence. */
970 if ((c & 0xf0) == 0xe0) {
971 /* Three-byte sequence. */
977 if ((c & 0xf8) == 0xf0) {
978 /* Four-byte sequence. */
984 if ((c & 0xc0) == 0x80) {
986 if (utf8_left == 0) {
991 utf8_partial = (utf8_partial << 8) | c;
992 if (utf8_left == 0) {
998 b = (u >> 24) & 0xff;
999 if (b != 0) { /* Four-byte sequence */
1001 b = (u >> 16) & 0xff;
1002 v = (v << 6) | (b & 0x3f);
1003 b = (u >> 8) & 0xff;
1004 v = (v << 6) | (b & 0x3f);
1006 v = (v << 6) | (b & 0x3f);
1007 } else if ((b = (u >> 16) & 0xff) != 0) {
1008 v = b & 0x0f; /* Three-byte sequence */
1009 b = (u >> 8) & 0xff;
1010 v = (v << 6) | (b & 0x3f);
1012 v = (v << 6) | (b & 0x3f);
1013 } else if ((b = (u >> 8) & 0xff) != 0) {
1014 v = b & 0x1f; /* Two-byte sequence */
1016 v = (v << 6) | (b & 0x3f);
1018 /* Send unicode char directly to console. */
1019 efi_cons_efiputchar(v);
1024 /* Anything left is illegal in UTF-8 sequence. */
1030 efi_cons_putchar(int c)
1032 unsigned char ch = c;
1035 * Don't use Teken when we're doing pure serial, or a multiple console
1036 * with video "primary" because that's also serial.
1038 if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || buffer == NULL) {
1043 teken_input(&teken, &ch, sizeof (ch));
1047 keybuf_getchar(void)
1051 for (i = 0; i < KEYBUFSZ; i++) {
1052 if (keybuf[i] != 0) {
1067 for (i = 0; i < KEYBUFSZ; i++) {
1075 * We are not reading input before keybuf is empty, so we are safe
1076 * just to fill keybuf from the beginning.
1079 keybuf_inschar(EFI_INPUT_KEY *key)
1082 switch (key->ScanCode) {
1083 case SCAN_UP: /* UP */
1084 keybuf[0] = 0x1b; /* esc */
1088 case SCAN_DOWN: /* DOWN */
1089 keybuf[0] = 0x1b; /* esc */
1093 case SCAN_RIGHT: /* RIGHT */
1094 keybuf[0] = 0x1b; /* esc */
1098 case SCAN_LEFT: /* LEFT */
1099 keybuf[0] = 0x1b; /* esc */
1104 keybuf[0] = CHAR_BACKSPACE;
1107 keybuf[0] = 0x1b; /* esc */
1110 keybuf[0] = key->UnicodeChar;
1121 status = conin->ReadKeyStroke(conin, &key);
1122 if (status == EFI_SUCCESS) {
1123 keybuf_inschar(&key);
1130 efi_readkey_ex(void)
1134 EFI_KEY_DATA key_data;
1137 status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1138 if (status == EFI_SUCCESS) {
1139 kss = key_data.KeyState.KeyShiftState;
1141 if (kss & EFI_SHIFT_STATE_VALID) {
1144 * quick mapping to control chars, replace with
1147 if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1148 kss & EFI_LEFT_CONTROL_PRESSED) {
1149 if (kp->UnicodeChar >= 'a' &&
1150 kp->UnicodeChar <= 'z') {
1151 kp->UnicodeChar -= 'a';
1157 * The shift state and/or toggle state may not be valid,
1158 * but we still can have ScanCode or UnicodeChar.
1160 if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1169 efi_cons_getchar(void)
1173 if ((c = keybuf_getchar()) != 0)
1178 if (coninex == NULL) {
1180 return (keybuf_getchar());
1182 if (efi_readkey_ex())
1183 return (keybuf_getchar());
1194 if (keybuf_ischar() || key_pending)
1198 * Some EFI implementation (u-boot for example) do not support
1200 * CheckEvent() can clear the signaled state.
1202 if (coninex != NULL) {
1203 if (coninex->WaitForKeyEx == NULL) {
1204 key_pending = efi_readkey_ex();
1206 status = BS->CheckEvent(coninex->WaitForKeyEx);
1207 key_pending = status == EFI_SUCCESS;
1210 if (conin->WaitForKey == NULL) {
1211 key_pending = efi_readkey();
1213 status = BS->CheckEvent(conin->WaitForKey);
1214 key_pending = status == EFI_SUCCESS;
1218 return (key_pending);
1221 /* Plain direct access to EFI OutputString(). */
1223 efi_cons_efiputchar(int c)
1229 buf[1] = 0; /* terminate string */
1231 status = conout->TestString(conout, buf);
1232 if (EFI_ERROR(status))
1234 conout->OutputString(conout, buf);