]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efi_console.c
loader: use display pixel density for font autoselection
[FreeBSD/FreeBSD.git] / stand / efi / libefi / efi_console.c
1 /*-
2  * Copyright (c) 2000 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <efi.h>
32 #include <efilib.h>
33 #include <teken.h>
34 #include <sys/reboot.h>
35 #include <machine/metadata.h>
36 #include <gfx_fb.h>
37 #include <framebuffer.h>
38 #include "bootstrap.h"
39
40 extern EFI_GUID gop_guid;
41 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
42 static SIMPLE_TEXT_OUTPUT_INTERFACE     *conout;
43 static SIMPLE_INPUT_INTERFACE           *conin;
44 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
45 static bool efi_started;
46
47 static int mode;                /* Does ConOut have serial console? */
48
49 static uint32_t utf8_left;
50 static uint32_t utf8_partial;
51 #ifdef TERM_EMU
52 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY
53 #define DEFAULT_BGCOLOR EFI_BLACK
54
55 #define MAXARGS 8
56 static int args[MAXARGS], argc;
57 static int fg_c, bg_c, curx, cury;
58 static int esc;
59
60 void get_pos(int *x, int *y);
61 void curs_move(int *_x, int *_y, int x, int y);
62 static void CL(int);
63 void HO(void);
64 void end_term(void);
65 #endif
66
67 #define TEXT_ROWS       24
68 #define TEXT_COLS       80
69
70 static tf_bell_t        efi_cons_bell;
71 static tf_cursor_t      efi_text_cursor;
72 static tf_putchar_t     efi_text_putchar;
73 static tf_fill_t        efi_text_fill;
74 static tf_copy_t        efi_text_copy;
75 static tf_param_t       efi_text_param;
76 static tf_respond_t     efi_cons_respond;
77
78 static teken_funcs_t tf = {
79         .tf_bell        = efi_cons_bell,
80         .tf_cursor      = efi_text_cursor,
81         .tf_putchar     = efi_text_putchar,
82         .tf_fill        = efi_text_fill,
83         .tf_copy        = efi_text_copy,
84         .tf_param       = efi_text_param,
85         .tf_respond     = efi_cons_respond,
86 };
87
88 static teken_funcs_t tfx = {
89         .tf_bell        = efi_cons_bell,
90         .tf_cursor      = gfx_fb_cursor,
91         .tf_putchar     = gfx_fb_putchar,
92         .tf_fill        = gfx_fb_fill,
93         .tf_copy        = gfx_fb_copy,
94         .tf_param       = gfx_fb_param,
95         .tf_respond     = efi_cons_respond,
96 };
97
98 #define KEYBUFSZ 10
99 static unsigned keybuf[KEYBUFSZ];       /* keybuf for extended codes */
100 static int key_pending;
101
102 static const unsigned char teken_color_to_efi_color[16] = {
103         EFI_BLACK,
104         EFI_RED,
105         EFI_GREEN,
106         EFI_BROWN,
107         EFI_BLUE,
108         EFI_MAGENTA,
109         EFI_CYAN,
110         EFI_LIGHTGRAY,
111         EFI_DARKGRAY,
112         EFI_LIGHTRED,
113         EFI_LIGHTGREEN,
114         EFI_YELLOW,
115         EFI_LIGHTBLUE,
116         EFI_LIGHTMAGENTA,
117         EFI_LIGHTCYAN,
118         EFI_WHITE
119 };
120
121 static void efi_cons_probe(struct console *);
122 static int efi_cons_init(int);
123 void efi_cons_putchar(int);
124 int efi_cons_getchar(void);
125 void efi_cons_efiputchar(int);
126 int efi_cons_poll(void);
127 static void cons_draw_frame(teken_attr_t *);
128
129 struct console efi_console = {
130         "efi",
131         "EFI console",
132         C_WIDEOUT,
133         efi_cons_probe,
134         efi_cons_init,
135         efi_cons_putchar,
136         efi_cons_getchar,
137         efi_cons_poll
138 };
139
140 /*
141  * This function is used to mark a rectangular image area so the scrolling
142  * will know we need to copy the data from there.
143  */
144 void
145 term_image_display(teken_gfx_t *state, const teken_rect_t *r)
146 {
147         teken_pos_t p;
148         int idx;
149
150         if (screen_buffer == NULL)
151                 return;
152
153         for (p.tp_row = r->tr_begin.tp_row;
154             p.tp_row < r->tr_end.tp_row; p.tp_row++) {
155                 for (p.tp_col = r->tr_begin.tp_col;
156                     p.tp_col < r->tr_end.tp_col; p.tp_col++) {
157                         idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
158                         if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
159                                 return;
160                         screen_buffer[idx].a.ta_format |= TF_IMAGE;
161                 }
162         }
163 }
164
165 /*
166  * Not implemented.
167  */
168 static void
169 efi_cons_bell(void *s __unused)
170 {
171 }
172
173 static void
174 efi_text_cursor(void *arg, const teken_pos_t *p)
175 {
176         teken_gfx_t *state = arg;
177         UINTN col, row;
178
179         row = p->tp_row;
180         if (p->tp_row >= state->tg_tp.tp_row)
181                 row = state->tg_tp.tp_row - 1;
182
183         col = p->tp_col;
184         if (p->tp_col >= state->tg_tp.tp_col)
185                 col = state->tg_tp.tp_col - 1;
186
187         conout->SetCursorPosition(conout, col, row);
188 }
189
190 static void
191 efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll)
192 {
193         UINTN a, attr;
194         struct text_pixel *px;
195         teken_color_t fg, bg, tmp;
196
197         px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col;
198         a = conout->Mode->Attribute;
199
200         fg = teken_256to16(px->a.ta_fgcolor);
201         bg = teken_256to16(px->a.ta_bgcolor);
202         if (px->a.ta_format & TF_BOLD)
203                 fg |= TC_LIGHT;
204         if (px->a.ta_format & TF_BLINK)
205                 bg |= TC_LIGHT;
206
207         if (px->a.ta_format & TF_REVERSE) {
208                 tmp = fg;
209                 fg = bg;
210                 bg = tmp;
211         }
212
213         attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg],
214             teken_color_to_efi_color[bg] & 0x7);
215
216         conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
217
218         /* to prevent autoscroll, skip print of lower right char */
219         if (!autoscroll &&
220             p->tp_row == state->tg_tp.tp_row - 1 &&
221             p->tp_col == state->tg_tp.tp_col - 1)
222                 return;
223
224         (void) conout->SetAttribute(conout, attr);
225         efi_cons_efiputchar(px->c);
226         (void) conout->SetAttribute(conout, a);
227 }
228
229 static void
230 efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c,
231     const teken_attr_t *a)
232 {
233         teken_gfx_t *state = s;
234         EFI_STATUS status;
235         int idx;
236
237         idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
238         if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
239                 return;
240
241         screen_buffer[idx].c = c;
242         screen_buffer[idx].a = *a;
243
244         efi_text_printchar(s, p, false);
245 }
246
247 static void
248 efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c,
249     const teken_attr_t *a)
250 {
251         teken_gfx_t *state = arg;
252         teken_pos_t p;
253
254         if (state->tg_cursor_visible)
255                 conout->EnableCursor(conout, FALSE);
256         for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
257             p.tp_row++)
258                 for (p.tp_col = r->tr_begin.tp_col;
259                     p.tp_col < r->tr_end.tp_col; p.tp_col++)
260                         efi_text_putchar(state, &p, c, a);
261         if (state->tg_cursor_visible)
262                 conout->EnableCursor(conout, TRUE);
263 }
264
265 static void
266 efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s,
267     teken_pos_t *d, bool scroll)
268 {
269         unsigned soffset, doffset;
270         teken_pos_t sp, dp;
271         int x;
272
273         soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
274         doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
275
276         sp = *s;
277         dp = *d;
278         for (x = 0; x < ncol; x++) {
279                 sp.tp_col = s->tp_col + x;
280                 dp.tp_col = d->tp_col + x;
281                 if (!is_same_pixel(&screen_buffer[soffset + x],
282                     &screen_buffer[doffset + x])) {
283                         screen_buffer[doffset + x] =
284                             screen_buffer[soffset + x];
285                         if (!scroll)
286                                 efi_text_printchar(state, &dp, false);
287                 } else if (scroll) {
288                         /* Draw last char and trigger scroll. */
289                         if (dp.tp_col + 1 == state->tg_tp.tp_col &&
290                             dp.tp_row + 1 == state->tg_tp.tp_row) {
291                                 efi_text_printchar(state, &dp, true);
292                         }
293                 }
294         }
295 }
296
297 static void
298 efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
299 {
300         teken_gfx_t *state = arg;
301         unsigned doffset, soffset;
302         teken_pos_t d, s;
303         int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
304         bool scroll = false;
305
306         /*
307          * Copying is a little tricky. We must make sure we do it in
308          * correct order, to make sure we don't overwrite our own data.
309          */
310
311         nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
312         ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
313
314         /*
315          * Check if we do copy whole screen.
316          */
317         if (p->tp_row == 0 && p->tp_col == 0 &&
318             nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2)
319                 scroll = true;
320
321         soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
322         doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
323
324         /* remove the cursor */
325         if (state->tg_cursor_visible)
326                 conout->EnableCursor(conout, FALSE);
327
328         /*
329          * Copy line by line.
330          */
331         if (doffset <= soffset) {
332                 s = r->tr_begin;
333                 d = *p;
334                 for (y = 0; y < nrow; y++) {
335                         s.tp_row = r->tr_begin.tp_row + y;
336                         d.tp_row = p->tp_row + y;
337
338                         efi_text_copy_line(state, ncol, &s, &d, scroll);
339                 }
340         } else {
341                 for (y = nrow - 1; y >= 0; y--) {
342                         s.tp_row = r->tr_begin.tp_row + y;
343                         d.tp_row = p->tp_row + y;
344
345                         efi_text_copy_line(state, ncol, &s, &d, false);
346                 }
347         }
348
349         /* display the cursor */
350         if (state->tg_cursor_visible)
351                 conout->EnableCursor(conout, TRUE);
352 }
353
354 static void
355 efi_text_param(void *arg, int cmd, unsigned int value)
356 {
357         teken_gfx_t *state = arg;
358
359         switch (cmd) {
360         case TP_SETLOCALCURSOR:
361                 /*
362                  * 0 means normal (usually block), 1 means hidden, and
363                  * 2 means blinking (always block) for compatibility with
364                  * syscons.  We don't support any changes except hiding,
365                  * so must map 2 to 0.
366                  */
367                 value = (value == 1) ? 0 : 1;
368                 /* FALLTHROUGH */
369         case TP_SHOWCURSOR:
370                 if (value != 0) {
371                         conout->EnableCursor(conout, TRUE);
372                         state->tg_cursor_visible = true;
373                 } else {
374                         conout->EnableCursor(conout, FALSE);
375                         state->tg_cursor_visible = false;
376                 }
377                 break;
378         default:
379                 /* Not yet implemented */
380                 break;
381         }
382 }
383
384 /*
385  * Not implemented.
386  */
387 static void
388 efi_cons_respond(void *s __unused, const void *buf __unused,
389     size_t len __unused)
390 {
391 }
392
393 /*
394  * Set up conin/conout/coninex to make sure we have input ready.
395  */
396 static void
397 efi_cons_probe(struct console *cp)
398 {
399         EFI_STATUS status;
400
401         conout = ST->ConOut;
402         conin = ST->ConIn;
403
404         /*
405          * Call SetMode to work around buggy firmware.
406          */
407         status = conout->SetMode(conout, conout->Mode->Mode);
408
409         if (coninex == NULL) {
410                 status = BS->OpenProtocol(ST->ConsoleInHandle,
411                     &simple_input_ex_guid, (void **)&coninex,
412                     IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
413                 if (status != EFI_SUCCESS)
414                         coninex = NULL;
415         }
416
417         cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
418 }
419
420 static bool
421 color_name_to_teken(const char *name, int *val)
422 {
423         if (strcasecmp(name, "black") == 0) {
424                 *val = TC_BLACK;
425                 return (true);
426         }
427         if (strcasecmp(name, "red") == 0) {
428                 *val = TC_RED;
429                 return (true);
430         }
431         if (strcasecmp(name, "green") == 0) {
432                 *val = TC_GREEN;
433                 return (true);
434         }
435         if (strcasecmp(name, "brown") == 0) {
436                 *val = TC_BROWN;
437                 return (true);
438         }
439         if (strcasecmp(name, "blue") == 0) {
440                 *val = TC_BLUE;
441                 return (true);
442         }
443         if (strcasecmp(name, "magenta") == 0) {
444                 *val = TC_MAGENTA;
445                 return (true);
446         }
447         if (strcasecmp(name, "cyan") == 0) {
448                 *val = TC_CYAN;
449                 return (true);
450         }
451         if (strcasecmp(name, "white") == 0) {
452                 *val = TC_WHITE;
453                 return (true);
454         }
455         return (false);
456 }
457
458 static int
459 efi_set_colors(struct env_var *ev, int flags, const void *value)
460 {
461         int val = 0;
462         char buf[2];
463         const void *evalue;
464         const teken_attr_t *ap;
465         teken_attr_t a;
466
467         if (value == NULL)
468                 return (CMD_OK);
469
470         if (color_name_to_teken(value, &val)) {
471                 snprintf(buf, sizeof (buf), "%d", val);
472                 evalue = buf;
473         } else {
474                 char *end;
475
476                 errno = 0;
477                 val = (int)strtol(value, &end, 0);
478                 if (errno != 0 || *end != '\0') {
479                         printf("Allowed values are either ansi color name or "
480                             "number from range [0-7].\n");
481                         return (CMD_OK);
482                 }
483                 evalue = value;
484         }
485
486         ap = teken_get_defattr(&gfx_state.tg_teken);
487         a = *ap;
488         if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
489                 /* is it already set? */
490                 if (ap->ta_fgcolor == val)
491                         return (CMD_OK);
492                 a.ta_fgcolor = val;
493         }
494         if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
495                 /* is it already set? */
496                 if (ap->ta_bgcolor == val)
497                         return (CMD_OK);
498                 a.ta_bgcolor = val;
499         }
500
501         /* Improve visibility */
502         if (a.ta_bgcolor == TC_WHITE)
503                 a.ta_bgcolor |= TC_LIGHT;
504
505         teken_set_defattr(&gfx_state.tg_teken, &a);
506         cons_draw_frame(&a);
507         env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
508         teken_input(&gfx_state.tg_teken, "\e[2J", 4);
509         return (CMD_OK);
510 }
511
512 #ifdef TERM_EMU
513 /* Get cursor position. */
514 void
515 get_pos(int *x, int *y)
516 {
517         *x = conout->Mode->CursorColumn;
518         *y = conout->Mode->CursorRow;
519 }
520
521 /* Move cursor to x rows and y cols (0-based). */
522 void
523 curs_move(int *_x, int *_y, int x, int y)
524 {
525         conout->SetCursorPosition(conout, x, y);
526         if (_x != NULL)
527                 *_x = conout->Mode->CursorColumn;
528         if (_y != NULL)
529                 *_y = conout->Mode->CursorRow;
530 }
531  
532 /* Clear internal state of the terminal emulation code. */
533 void
534 end_term(void)
535 {
536         esc = 0;
537         argc = -1;
538 }
539 #endif
540
541 static void
542 efi_cons_rawputchar(int c)
543 {
544         int i;
545         UINTN x, y;
546         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
547
548         if (c == '\t') {
549                 int n;
550
551                 n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
552                 for (i = 0; i < n; i++)
553                         efi_cons_rawputchar(' ');
554         } else {
555 #ifndef TERM_EMU
556                 if (c == '\n')
557                         efi_cons_efiputchar('\r');
558                 efi_cons_efiputchar(c);
559 #else
560                 switch (c) {
561                 case '\r':
562                         curx = 0;
563                         efi_cons_efiputchar('\r');
564                         return;
565                 case '\n':
566                         efi_cons_efiputchar('\n');
567                         efi_cons_efiputchar('\r');
568                         cury++;
569                         if (cury >= y)
570                                 cury--;
571                         curx = 0;
572                         return;
573                 case '\b':
574                         if (curx > 0) {
575                                 efi_cons_efiputchar('\b');
576                                 curx--;
577                         }
578                         return;
579                 default:
580                         efi_cons_efiputchar(c);
581                         curx++;
582                         if (curx > x-1) {
583                                 curx = 0;
584                                 cury++;
585                         }
586                         if (cury > y-1) {
587                                 curx = 0;
588                                 cury--;
589                         }
590                 }
591 #endif
592         }
593         conout->EnableCursor(conout, TRUE);
594 }
595
596 #ifdef TERM_EMU
597 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
598 static void
599 bail_out(int c)
600 {
601         char buf[16], *ch;
602         int i;
603
604         if (esc) {
605                 efi_cons_rawputchar('\033');
606                 if (esc != '\033')
607                         efi_cons_rawputchar(esc);
608                 for (i = 0; i <= argc; ++i) {
609                         sprintf(buf, "%d", args[i]);
610                         ch = buf;
611                         while (*ch)
612                                 efi_cons_rawputchar(*ch++);
613                 }
614         }
615         efi_cons_rawputchar(c);
616         end_term();
617 }
618
619 /* Clear display from current position to end of screen. */
620 static void
621 CD(void)
622 {
623         int i;
624         UINTN x, y;
625
626         get_pos(&curx, &cury);
627         if (curx == 0 && cury == 0) {
628                 conout->ClearScreen(conout);
629                 end_term();
630                 return;
631         }
632
633         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
634         CL(0);  /* clear current line from cursor to end */
635         for (i = cury + 1; i < y-1; i++) {
636                 curs_move(NULL, NULL, 0, i);
637                 CL(0);
638         }
639         curs_move(NULL, NULL, curx, cury);
640         end_term();
641 }
642
643 /*
644  * Absolute cursor move to args[0] rows and args[1] columns
645  * (the coordinates are 1-based).
646  */
647 static void
648 CM(void)
649 {
650         if (args[0] > 0)
651                 args[0]--;
652         if (args[1] > 0)
653                 args[1]--;
654         curs_move(&curx, &cury, args[1], args[0]);
655         end_term();
656 }
657
658 /* Home cursor (left top corner), also called from mode command. */
659 void
660 HO(void)
661 {
662         argc = 1;
663         args[0] = args[1] = 1;
664         CM();
665 }
666  
667 /* Clear line from current position to end of line */
668 static void
669 CL(int direction)
670 {
671         int i, len;
672         UINTN x, y;
673         CHAR16 *line;
674
675         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
676         switch (direction) {
677         case 0: /* from cursor to end */
678                 len = x - curx + 1;
679                 break;
680         case 1: /* from beginning to cursor */
681                 len = curx;
682                 break;
683         case 2: /* entire line */
684                 len = x;
685                 break;
686         default:        /* NOTREACHED */
687                 __unreachable();
688         }
689  
690         if (cury == y - 1)
691                 len--;
692
693         line = malloc(len * sizeof (CHAR16));
694         if (line == NULL) {
695                 printf("out of memory\n");
696                 return;
697         }
698         for (i = 0; i < len; i++)
699                 line[i] = ' ';
700         line[len-1] = 0;
701
702         if (direction != 0)
703                 curs_move(NULL, NULL, 0, cury);
704  
705         conout->OutputString(conout, line);
706         /* restore cursor position */
707         curs_move(NULL, NULL, curx, cury);
708         free(line);
709         end_term();
710 }
711
712 static void
713 get_arg(int c)
714 {
715         if (argc < 0)
716                 argc = 0;
717         args[argc] *= 10;
718         args[argc] += c - '0';
719 }
720 #endif
721  
722 /* Emulate basic capabilities of cons25 terminal */
723 static void
724 efi_term_emu(int c)
725 {
726 #ifdef TERM_EMU
727         static int ansi_col[] = {
728                 0, 4, 2, 6, 1, 5, 3, 7
729         };
730         int t, i;
731         EFI_STATUS status;
732  
733         switch (esc) {
734         case 0:
735                 switch (c) {
736                 case '\033':
737                         esc = c;
738                         break;
739                 default:
740                         efi_cons_rawputchar(c);
741                         break;
742                 }
743                 break;
744         case '\033':
745                 switch (c) {
746                 case '[':
747                         esc = c;
748                         args[0] = 0;
749                         argc = -1;
750                         break;
751                 default:
752                         bail_out(c);
753                         break;
754                 }
755                 break;
756         case '[':
757                 switch (c) {
758                 case ';':
759                         if (argc < 0)
760                                 argc = 0;
761                         else if (argc + 1 >= MAXARGS)
762                                 bail_out(c);
763                         else
764                                 args[++argc] = 0;
765                         break;
766                 case 'H':               /* ho = \E[H */
767                         if (argc < 0)
768                                 HO();
769                         else if (argc == 1)
770                                 CM();
771                         else
772                                 bail_out(c);
773                         break;
774                 case 'J':               /* cd = \E[J */
775                         if (argc < 0)
776                                 CD();
777                         else
778                                 bail_out(c);
779                         break;
780                 case 'm':
781                         if (argc < 0) {
782                                 fg_c = DEFAULT_FGCOLOR;
783                                 bg_c = DEFAULT_BGCOLOR;
784                         }
785                         for (i = 0; i <= argc; ++i) {
786                                 switch (args[i]) {
787                                 case 0:         /* back to normal */
788                                         fg_c = DEFAULT_FGCOLOR;
789                                         bg_c = DEFAULT_BGCOLOR;
790                                         break;
791                                 case 1:         /* bold */
792                                         fg_c |= 0x8;
793                                         break;
794                                 case 4:         /* underline */
795                                 case 5:         /* blink */
796                                         bg_c |= 0x8;
797                                         break;
798                                 case 7:         /* reverse */
799                                         t = fg_c;
800                                         fg_c = bg_c;
801                                         bg_c = t;
802                                         break;
803                                 case 22:        /* normal intensity */
804                                         fg_c &= ~0x8;
805                                         break;
806                                 case 24:        /* not underline */
807                                 case 25:        /* not blinking */
808                                         bg_c &= ~0x8;
809                                         break;
810                                 case 30: case 31: case 32: case 33:
811                                 case 34: case 35: case 36: case 37:
812                                         fg_c = ansi_col[args[i] - 30];
813                                         break;
814                                 case 39:        /* normal */
815                                         fg_c = DEFAULT_FGCOLOR;
816                                         break;
817                                 case 40: case 41: case 42: case 43:
818                                 case 44: case 45: case 46: case 47:
819                                         bg_c = ansi_col[args[i] - 40];
820                                         break;
821                                 case 49:        /* normal */
822                                         bg_c = DEFAULT_BGCOLOR;
823                                         break;
824                                 }
825                         }
826                         conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
827                         end_term();
828                         break;
829                 default:
830                         if (isdigit(c))
831                                 get_arg(c);
832                         else
833                                 bail_out(c);
834                         break;
835                 }
836                 break;
837         default:
838                 bail_out(c);
839                 break;
840         }
841 #else
842         efi_cons_rawputchar(c);
843 #endif
844 }
845
846 static int
847 env_screen_nounset(struct env_var *ev __unused)
848 {
849         if (gfx_state.tg_fb_type == FB_TEXT)
850                 return (0);
851         return (EPERM);
852 }
853
854 static void
855 cons_draw_frame(teken_attr_t *a)
856 {
857         teken_attr_t attr = *a;
858         teken_color_t fg = a->ta_fgcolor;
859
860         attr.ta_fgcolor = attr.ta_bgcolor;
861         teken_set_defattr(&gfx_state.tg_teken, &attr);
862
863         gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width,
864             gfx_state.tg_origin.tp_row, 1);
865         gfx_fb_drawrect(0,
866             gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1,
867             gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1);
868         gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row,
869             gfx_state.tg_origin.tp_col,
870             gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1);
871         gfx_fb_drawrect(
872             gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1,
873             gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width,
874             gfx_state.tg_fb.fb_height, 1);
875
876         attr.ta_fgcolor = fg;
877         teken_set_defattr(&gfx_state.tg_teken, &attr);
878 }
879
880 bool
881 cons_update_mode(bool use_gfx_mode)
882 {
883         UINTN cols, rows;
884         const teken_attr_t *a;
885         teken_attr_t attr;
886         EFI_STATUS status;
887         char env[10], *ptr;
888
889         if (!efi_started)
890                 return (false);
891
892         /*
893          * Despite the use_gfx_mode, we want to make sure we call
894          * efi_find_framebuffer(). This will populate the fb data,
895          * which will be passed to kernel.
896          */
897         if (efi_find_framebuffer(&gfx_state) == 0 && use_gfx_mode) {
898                 int roff, goff, boff;
899
900                 roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
901                 goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
902                 boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
903
904                 (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB,
905                     gfx_state.tg_fb.fb_mask_red >> roff, roff,
906                     gfx_state.tg_fb.fb_mask_green >> goff, goff,
907                     gfx_state.tg_fb.fb_mask_blue >> boff, boff);
908         } else {
909                 /*
910                  * Either text mode was asked by user or we failed to
911                  * find frame buffer.
912                  */
913                 gfx_state.tg_fb_type = FB_TEXT;
914         }
915
916         status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
917         if (EFI_ERROR(status) || cols * rows == 0) {
918                 cols = TEXT_COLS;
919                 rows = TEXT_ROWS;
920         }
921
922         /*
923          * When we have serial port listed in ConOut, use pre-teken emulator,
924          * if built with.
925          * The problem is, we can not output text on efi and comconsole when
926          * efi also has comconsole bound. But then again, we need to have
927          * terminal emulator for efi text mode to support the menu.
928          * While teken is too expensive to be used on serial console, the
929          * pre-teken emulator is light enough to be used on serial console.
930          *
931          * When doing multiple consoles (both serial and video),
932          * also just use the old emulator. RB_MULTIPLE also implies
933          * we're using a serial console.
934          */
935         mode = parse_uefi_con_out();
936         if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
937                 conout->EnableCursor(conout, FALSE);
938                 gfx_state.tg_cursor_visible = false;
939
940                 if (gfx_state.tg_fb_type == FB_TEXT) {
941
942                         gfx_state.tg_functions = &tf;
943                         /* ensure the following are not set for text mode */
944                         unsetenv("screen.height");
945                         unsetenv("screen.width");
946                         unsetenv("screen.depth");
947                 } else {
948                         uint32_t fb_height, fb_width;
949
950                         fb_height = gfx_state.tg_fb.fb_height;
951                         fb_width = gfx_state.tg_fb.fb_width;
952
953                         /*
954                          * setup_font() can adjust terminal size.
955                          * We can see two kind of bad happening.
956                          * We either can get too small console font - requested
957                          * terminal size is large, display resolution is
958                          * large, and we get very small font.
959                          * Or, we can get too large font - requested
960                          * terminal size is small and this will cause large
961                          * font to be selected.
962                          * Now, the setup_font() is updated to consider
963                          * display density and this should give us mostly
964                          * acceptable font. However, the catch is, not all
965                          * display devices will give us display density.
966                          * Still, we do hope, external monitors do - this is
967                          * where the display size will matter the most.
968                          * And for laptop screens, we should still get good
969                          * results by requesting 80x25 terminal.
970                          */
971                         gfx_state.tg_tp.tp_row = 25;
972                         gfx_state.tg_tp.tp_col = 80;
973                         setup_font(&gfx_state, fb_height, fb_width);
974                         rows = gfx_state.tg_tp.tp_row;
975                         cols = gfx_state.tg_tp.tp_col;
976                         /* Point of origin in pixels. */
977                         gfx_state.tg_origin.tp_row = (fb_height -
978                             (rows * gfx_state.tg_font.vf_height)) / 2;
979                         gfx_state.tg_origin.tp_col = (fb_width -
980                             (cols * gfx_state.tg_font.vf_width)) / 2;
981
982                         /* UEFI gop has depth 32. */
983                         gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height *
984                             gfx_state.tg_font.vf_width * 4;
985                         free(gfx_state.tg_glyph);
986                         gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size);
987                         if (gfx_state.tg_glyph == NULL)
988                                 return (false);
989
990                         gfx_state.tg_functions = &tfx;
991                         snprintf(env, sizeof (env), "%d", fb_height);
992                         env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK,
993                             env, env_noset, env_screen_nounset);
994                         snprintf(env, sizeof (env), "%d", fb_width);
995                         env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK,
996                             env, env_noset, env_screen_nounset);
997                         snprintf(env, sizeof (env), "%d",
998                             gfx_state.tg_fb.fb_bpp);
999                         env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK,
1000                             env, env_noset, env_screen_nounset);
1001                 }
1002
1003                 /* Record our terminal screen size. */
1004                 gfx_state.tg_tp.tp_row = rows;
1005                 gfx_state.tg_tp.tp_col = cols;
1006
1007                 teken_init(&gfx_state.tg_teken, gfx_state.tg_functions,
1008                     &gfx_state);
1009
1010                 free(screen_buffer);
1011                 screen_buffer = malloc(rows * cols * sizeof(*screen_buffer));
1012                 if (screen_buffer != NULL) {
1013                         teken_set_winsize(&gfx_state.tg_teken,
1014                             &gfx_state.tg_tp);
1015                         a = teken_get_defattr(&gfx_state.tg_teken);
1016                         attr = *a;
1017
1018                         /*
1019                          * On first run, we set up the efi_set_colors()
1020                          * callback. If the env is already set, we
1021                          * pick up fg and bg color values from the environment.
1022                          */
1023                         ptr = getenv("teken.fg_color");
1024                         if (ptr != NULL) {
1025                                 attr.ta_fgcolor = strtol(ptr, NULL, 10);
1026                                 ptr = getenv("teken.bg_color");
1027                                 attr.ta_bgcolor = strtol(ptr, NULL, 10);
1028
1029                                 teken_set_defattr(&gfx_state.tg_teken, &attr);
1030                         } else {
1031                                 snprintf(env, sizeof(env), "%d",
1032                                     attr.ta_fgcolor);
1033                                 env_setenv("teken.fg_color", EV_VOLATILE, env,
1034                                     efi_set_colors, env_nounset);
1035                                 snprintf(env, sizeof(env), "%d",
1036                                     attr.ta_bgcolor);
1037                                 env_setenv("teken.bg_color", EV_VOLATILE, env,
1038                                     efi_set_colors, env_nounset);
1039                         }
1040                 }
1041         }
1042
1043         if (screen_buffer == NULL) {
1044                 conout->EnableCursor(conout, TRUE);
1045 #ifdef TERM_EMU
1046                 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
1047                     DEFAULT_BGCOLOR));
1048                 end_term();
1049                 get_pos(&curx, &cury);
1050                 curs_move(&curx, &cury, curx, cury);
1051                 fg_c = DEFAULT_FGCOLOR;
1052                 bg_c = DEFAULT_BGCOLOR;
1053 #endif
1054         } else {
1055                 /* Improve visibility */
1056                 if (attr.ta_bgcolor == TC_WHITE)
1057                         attr.ta_bgcolor |= TC_LIGHT;
1058                 teken_set_defattr(&gfx_state.tg_teken, &attr);
1059
1060                 /* Draw frame around terminal area. */
1061                 cons_draw_frame(&attr);
1062                 /*
1063                  * Erase display, this will also fill our screen
1064                  * buffer.
1065                  */
1066                 teken_input(&gfx_state.tg_teken, "\e[2J", 4);
1067                 gfx_state.tg_functions->tf_param(&gfx_state,
1068                     TP_SHOWCURSOR, 1);
1069         }
1070
1071         snprintf(env, sizeof (env), "%u", (unsigned)rows);
1072         setenv("LINES", env, 1);
1073         snprintf(env, sizeof (env), "%u", (unsigned)cols);
1074         setenv("COLUMNS", env, 1);
1075
1076         return (true);
1077 }
1078
1079 static int
1080 efi_cons_init(int arg)
1081 {
1082         EFI_STATUS status;
1083
1084         if (efi_started)
1085                 return (0);
1086
1087         efi_started = true;
1088
1089         gfx_framework_init();
1090         if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT))
1091                 return (0);
1092
1093         return (1);
1094 }
1095
1096 static void
1097 input_partial(void)
1098 {
1099         unsigned i;
1100         uint32_t c;
1101
1102         if (utf8_left == 0)
1103                 return;
1104
1105         for (i = 0; i < sizeof(utf8_partial); i++) {
1106                 c = (utf8_partial >> (24 - (i << 3))) & 0xff;
1107                 if (c != 0)
1108                         efi_term_emu(c);
1109         }
1110         utf8_left = 0;
1111         utf8_partial = 0;
1112 }
1113
1114 static void
1115 input_byte(uint8_t c)
1116 {
1117         if ((c & 0x80) == 0x00) {
1118                 /* One-byte sequence. */
1119                 input_partial();
1120                 efi_term_emu(c);
1121                 return;
1122         }
1123         if ((c & 0xe0) == 0xc0) {
1124                 /* Two-byte sequence. */
1125                 input_partial();
1126                 utf8_left = 1;
1127                 utf8_partial = c;
1128                 return;
1129         }
1130         if ((c & 0xf0) == 0xe0) {
1131                 /* Three-byte sequence. */
1132                 input_partial();
1133                 utf8_left = 2;
1134                 utf8_partial = c;
1135                 return;
1136         }
1137         if ((c & 0xf8) == 0xf0) {
1138                 /* Four-byte sequence. */
1139                 input_partial();
1140                 utf8_left = 3;
1141                 utf8_partial = c;
1142                 return;
1143         }
1144         if ((c & 0xc0) == 0x80) {
1145                 /* Invalid state? */
1146                 if (utf8_left == 0) {
1147                         efi_term_emu(c);
1148                         return;
1149                 }
1150                 utf8_left--;
1151                 utf8_partial = (utf8_partial << 8) | c;
1152                 if (utf8_left == 0) {
1153                         uint32_t v, u;
1154                         uint8_t b;
1155
1156                         v = 0;
1157                         u = utf8_partial;
1158                         b = (u >> 24) & 0xff;
1159                         if (b != 0) {           /* Four-byte sequence */
1160                                 v = b & 0x07;
1161                                 b = (u >> 16) & 0xff;
1162                                 v = (v << 6) | (b & 0x3f);
1163                                 b = (u >> 8) & 0xff;
1164                                 v = (v << 6) | (b & 0x3f);
1165                                 b = u & 0xff;
1166                                 v = (v << 6) | (b & 0x3f);
1167                         } else if ((b = (u >> 16) & 0xff) != 0) {
1168                                 v = b & 0x0f;   /* Three-byte sequence */
1169                                 b = (u >> 8) & 0xff;
1170                                 v = (v << 6) | (b & 0x3f);
1171                                 b = u & 0xff;
1172                                 v = (v << 6) | (b & 0x3f);
1173                         } else if ((b = (u >> 8) & 0xff) != 0) {
1174                                 v = b & 0x1f;   /* Two-byte sequence */
1175                                 b = u & 0xff;
1176                                 v = (v << 6) | (b & 0x3f);
1177                         }
1178                         /* Send unicode char directly to console. */
1179                         efi_cons_efiputchar(v);
1180                         utf8_partial = 0;
1181                 }
1182                 return;
1183         }
1184         /* Anything left is illegal in UTF-8 sequence. */
1185         input_partial();
1186         efi_term_emu(c);
1187 }
1188
1189 void
1190 efi_cons_putchar(int c)
1191 {
1192         unsigned char ch = c;
1193
1194         /*
1195          * Don't use Teken when we're doing pure serial, or a multiple console
1196          * with video "primary" because that's also serial.
1197          */
1198         if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) {
1199                 input_byte(ch);
1200                 return;
1201         }
1202
1203         teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
1204 }
1205
1206 static int
1207 keybuf_getchar(void)
1208 {
1209         int i, c = 0;
1210
1211         for (i = 0; i < KEYBUFSZ; i++) {
1212                 if (keybuf[i] != 0) {
1213                         c = keybuf[i];
1214                         keybuf[i] = 0;
1215                         break;
1216                 }
1217         }
1218
1219         return (c);
1220 }
1221
1222 static bool
1223 keybuf_ischar(void)
1224 {
1225         int i;
1226
1227         for (i = 0; i < KEYBUFSZ; i++) {
1228                 if (keybuf[i] != 0)
1229                         return (true);
1230         }
1231         return (false);
1232 }
1233
1234 /*
1235  * We are not reading input before keybuf is empty, so we are safe
1236  * just to fill keybuf from the beginning.
1237  */
1238 static void
1239 keybuf_inschar(EFI_INPUT_KEY *key)
1240 {
1241
1242         switch (key->ScanCode) {
1243         case SCAN_UP: /* UP */
1244                 keybuf[0] = 0x1b;       /* esc */
1245                 keybuf[1] = '[';
1246                 keybuf[2] = 'A';
1247                 break;
1248         case SCAN_DOWN: /* DOWN */
1249                 keybuf[0] = 0x1b;       /* esc */
1250                 keybuf[1] = '[';
1251                 keybuf[2] = 'B';
1252                 break;
1253         case SCAN_RIGHT: /* RIGHT */
1254                 keybuf[0] = 0x1b;       /* esc */
1255                 keybuf[1] = '[';
1256                 keybuf[2] = 'C';
1257                 break;
1258         case SCAN_LEFT: /* LEFT */
1259                 keybuf[0] = 0x1b;       /* esc */
1260                 keybuf[1] = '[';
1261                 keybuf[2] = 'D';
1262                 break;
1263         case SCAN_DELETE:
1264                 keybuf[0] = CHAR_BACKSPACE;
1265                 break;
1266         case SCAN_ESC:
1267                 keybuf[0] = 0x1b;       /* esc */
1268                 break;
1269         default:
1270                 keybuf[0] = key->UnicodeChar;
1271                 break;
1272         }
1273 }
1274
1275 static bool
1276 efi_readkey(void)
1277 {
1278         EFI_STATUS status;
1279         EFI_INPUT_KEY key;
1280
1281         status = conin->ReadKeyStroke(conin, &key);
1282         if (status == EFI_SUCCESS) {
1283                 keybuf_inschar(&key);
1284                 return (true);
1285         }
1286         return (false);
1287 }
1288
1289 static bool
1290 efi_readkey_ex(void)
1291 {
1292         EFI_STATUS status;
1293         EFI_INPUT_KEY *kp;
1294         EFI_KEY_DATA  key_data;
1295         uint32_t kss;
1296
1297         status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1298         if (status == EFI_SUCCESS) {
1299                 kss = key_data.KeyState.KeyShiftState;
1300                 kp = &key_data.Key;
1301                 if (kss & EFI_SHIFT_STATE_VALID) {
1302
1303                         /*
1304                          * quick mapping to control chars, replace with
1305                          * map lookup later.
1306                          */
1307                         if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1308                             kss & EFI_LEFT_CONTROL_PRESSED) {
1309                                 if (kp->UnicodeChar >= 'a' &&
1310                                     kp->UnicodeChar <= 'z') {
1311                                         kp->UnicodeChar -= 'a';
1312                                         kp->UnicodeChar++;
1313                                 }
1314                         }
1315                 }
1316                 /*
1317                  * The shift state and/or toggle state may not be valid,
1318                  * but we still can have ScanCode or UnicodeChar.
1319                  */
1320                 if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1321                         return (false);
1322                 keybuf_inschar(kp);
1323                 return (true);
1324         }
1325         return (false);
1326 }
1327
1328 int
1329 efi_cons_getchar(void)
1330 {
1331         int c;
1332
1333         if ((c = keybuf_getchar()) != 0)
1334                 return (c);
1335
1336         key_pending = 0;
1337
1338         if (coninex == NULL) {
1339                 if (efi_readkey())
1340                         return (keybuf_getchar());
1341         } else {
1342                 if (efi_readkey_ex())
1343                         return (keybuf_getchar());
1344         }
1345
1346         return (-1);
1347 }
1348
1349 int
1350 efi_cons_poll(void)
1351 {
1352         EFI_STATUS status;
1353
1354         if (keybuf_ischar() || key_pending)
1355                 return (1);
1356
1357         /*
1358          * Some EFI implementation (u-boot for example) do not support
1359          * WaitForKey().
1360          * CheckEvent() can clear the signaled state.
1361          */
1362         if (coninex != NULL) {
1363                 if (coninex->WaitForKeyEx == NULL) {
1364                         key_pending = efi_readkey_ex();
1365                 } else {
1366                         status = BS->CheckEvent(coninex->WaitForKeyEx);
1367                         key_pending = status == EFI_SUCCESS;
1368                 }
1369         } else {
1370                 if (conin->WaitForKey == NULL) {
1371                         key_pending = efi_readkey();
1372                 } else {
1373                         status = BS->CheckEvent(conin->WaitForKey);
1374                         key_pending = status == EFI_SUCCESS;
1375                 }
1376         }
1377
1378         return (key_pending);
1379 }
1380
1381 /* Plain direct access to EFI OutputString(). */
1382 void
1383 efi_cons_efiputchar(int c)
1384 {
1385         CHAR16 buf[2];
1386         EFI_STATUS status;
1387
1388         buf[0] = c;
1389         buf[1] = 0;     /* terminate string */
1390
1391         status = conout->TestString(conout, buf);
1392         if (EFI_ERROR(status))
1393                 buf[0] = '?';
1394         conout->OutputString(conout, buf);
1395 }