]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efi_console.c
Merge OpenSSL 1.1.1e.
[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 <efi.h>
31 #include <efilib.h>
32 #include <teken.h>
33 #include <sys/reboot.h>
34
35 #include "bootstrap.h"
36
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;
41
42 static int mode;                /* Does ConOut have serial console? */
43
44 static uint32_t utf8_left;
45 static uint32_t utf8_partial;
46 #ifdef TERM_EMU
47 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY
48 #define DEFAULT_BGCOLOR EFI_BLACK
49
50 #define MAXARGS 8
51 static int args[MAXARGS], argc;
52 static int fg_c, bg_c, curx, cury;
53 static int esc;
54
55 void get_pos(int *x, int *y);
56 void curs_move(int *_x, int *_y, int x, int y);
57 static void CL(int);
58 void HO(void);
59 void end_term(void);
60 #endif
61
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;
69
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,
78 };
79
80 teken_t teken;
81 teken_pos_t tp;
82
83 struct text_pixel {
84         teken_char_t c;
85         teken_attr_t a;
86 };
87
88 static struct text_pixel *buffer;
89
90 #define KEYBUFSZ 10
91 static unsigned keybuf[KEYBUFSZ];       /* keybuf for extended codes */
92 static int key_pending;
93
94 static const unsigned char teken_color_to_efi_color[16] = {
95         EFI_BLACK,
96         EFI_RED,
97         EFI_GREEN,
98         EFI_BROWN,
99         EFI_BLUE,
100         EFI_MAGENTA,
101         EFI_CYAN,
102         EFI_LIGHTGRAY,
103         EFI_DARKGRAY,
104         EFI_LIGHTRED,
105         EFI_LIGHTGREEN,
106         EFI_YELLOW,
107         EFI_LIGHTBLUE,
108         EFI_LIGHTMAGENTA,
109         EFI_LIGHTCYAN,
110         EFI_WHITE
111 };
112
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);
119
120 struct console efi_console = {
121         "efi",
122         "EFI console",
123         C_WIDEOUT,
124         efi_cons_probe,
125         efi_cons_init,
126         efi_cons_putchar,
127         efi_cons_getchar,
128         efi_cons_poll
129 };
130
131 /*
132  * Not implemented.
133  */
134 static void
135 efi_cons_bell(void *s __unused)
136 {
137 }
138
139 static void
140 efi_text_cursor(void *s __unused, const teken_pos_t *p)
141 {
142         UINTN row, col;
143
144         (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
145
146         if (p->tp_col == col)
147                 col = p->tp_col - 1;
148         else
149                 col = p->tp_col;
150
151         if (p->tp_row == row)
152                 row = p->tp_row - 1;
153         else
154                 row = p->tp_row;
155
156         conout->SetCursorPosition(conout, col, row);
157 }
158
159 static void
160 efi_text_printchar(const teken_pos_t *p, bool autoscroll)
161 {
162         UINTN a, attr;
163         struct text_pixel *px;
164         teken_color_t fg, bg, tmp;
165
166         px = buffer + p->tp_col + p->tp_row * tp.tp_col;
167         a = conout->Mode->Attribute;
168
169         fg = teken_256to16(px->a.ta_fgcolor);
170         bg = teken_256to16(px->a.ta_bgcolor);
171         if (px->a.ta_format & TF_BOLD)
172                 fg |= TC_LIGHT;
173         if (px->a.ta_format & TF_BLINK)
174                 bg |= TC_LIGHT;
175
176         if (px->a.ta_format & TF_REVERSE) {
177                 tmp = fg;
178                 fg = bg;
179                 bg = tmp;
180         }
181
182         attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg],
183             teken_color_to_efi_color[bg] & 0x7);
184
185         conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
186
187         /* to prvent autoscroll, skip print of lower right char */
188         if (!autoscroll &&
189             p->tp_row == tp.tp_row - 1 &&
190             p->tp_col == tp.tp_col - 1)
191                 return;
192
193         (void) conout->SetAttribute(conout, attr);
194         efi_cons_efiputchar(px->c);
195         (void) conout->SetAttribute(conout, a);
196 }
197
198 static void
199 efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c,
200     const teken_attr_t *a)
201 {
202         EFI_STATUS status;
203         int idx;
204
205         idx = p->tp_col + p->tp_row * tp.tp_col;
206         buffer[idx].c = c;
207         buffer[idx].a = *a;
208         efi_text_printchar(p, false);
209 }
210
211 static void
212 efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
213     const teken_attr_t *a)
214 {
215         teken_pos_t p;
216         UINTN row, col;
217
218         (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
219
220         conout->EnableCursor(conout, FALSE);
221         for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
222             p.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);
227 }
228
229 static bool
230 efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
231 {
232         if (px1->c != px2->c)
233                 return (false);
234
235         if (px1->a.ta_format != px2->a.ta_format)
236                 return (false);
237         if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
238                 return (false);
239         if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
240                 return (false);
241
242         return (true);
243 }
244
245 static void
246 efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
247 {
248         int srow, drow;
249         int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
250         teken_pos_t d, s;
251         bool scroll = false;
252
253         /*
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.
256          */
257
258         nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
259         ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
260
261         /*
262          * Check if we do copy whole screen.
263          */
264         if (p->tp_row == 0 && p->tp_col == 0 &&
265             nrow == tp.tp_row - 2 && ncol == tp.tp_col - 2)
266                 scroll = true;
267
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;
279
280                                 if (!efi_same_pixel(
281                                     &buffer[d.tp_col + drow],
282                                     &buffer[s.tp_col + srow])) {
283                                         buffer[d.tp_col + drow] =
284                                             buffer[s.tp_col + srow];
285                                         if (!scroll)
286                                                 efi_text_printchar(&d, false);
287                                 } else if (scroll) {
288                                         /*
289                                          * Draw last char and trigger
290                                          * scroll.
291                                          */
292                                         if (y == nrow - 1 &&
293                                             x == ncol - 1) {
294                                                 efi_text_printchar(&d, true);
295                                         }
296                                 }
297                         }
298                 }
299         } else {
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;
311
312                                         if (!efi_same_pixel(
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);
318                                         }
319                                 }
320                         }
321                 } else {
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;
331
332                                         if (!efi_same_pixel(
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);
338                                         }
339                                 }
340                         }
341                 }
342         }
343         conout->EnableCursor(conout, TRUE);
344 }
345
346 static void
347 efi_text_param(void *s __unused, int cmd, unsigned int value)
348 {
349         switch (cmd) {
350         case TP_SETLOCALCURSOR:
351                 /*
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.
356                  */
357                 value = (value == 1) ? 0 : 1;
358                 /* FALLTHROUGH */
359         case TP_SHOWCURSOR:
360                 if (value == 1)
361                         conout->EnableCursor(conout, TRUE);
362                 else
363                         conout->EnableCursor(conout, FALSE);
364                 break;
365         default:
366                 /* Not yet implemented */
367                 break;
368         }
369 }
370
371 /*
372  * Not implemented.
373  */
374 static void
375 efi_cons_respond(void *s __unused, const void *buf __unused,
376     size_t len __unused)
377 {
378 }
379
380 /*
381  * Set up conin/conout/coninex to make sure we have input ready.
382  */
383 static void
384 efi_cons_probe(struct console *cp)
385 {
386         EFI_STATUS status;
387
388         conout = ST->ConOut;
389         conin = ST->ConIn;
390
391         status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
392             (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
393         if (status != EFI_SUCCESS)
394                 coninex = NULL;
395
396         cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
397 }
398
399 static bool
400 color_name_to_teken(const char *name, int *val)
401 {
402         if (strcasecmp(name, "black") == 0) {
403                 *val = TC_BLACK;
404                 return (true);
405         }
406         if (strcasecmp(name, "red") == 0) {
407                 *val = TC_RED;
408                 return (true);
409         }
410         if (strcasecmp(name, "green") == 0) {
411                 *val = TC_GREEN;
412                 return (true);
413         }
414         if (strcasecmp(name, "brown") == 0) {
415                 *val = TC_BROWN;
416                 return (true);
417         }
418         if (strcasecmp(name, "blue") == 0) {
419                 *val = TC_BLUE;
420                 return (true);
421         }
422         if (strcasecmp(name, "magenta") == 0) {
423                 *val = TC_MAGENTA;
424                 return (true);
425         }
426         if (strcasecmp(name, "cyan") == 0) {
427                 *val = TC_CYAN;
428                 return (true);
429         }
430         if (strcasecmp(name, "white") == 0) {
431                 *val = TC_WHITE;
432                 return (true);
433         }
434         return (false);
435 }
436
437 static int
438 efi_set_colors(struct env_var *ev, int flags, const void *value)
439 {
440         int val = 0;
441         char buf[2];
442         const void *evalue;
443         const teken_attr_t *ap;
444         teken_attr_t a;
445
446         if (value == NULL)
447                 return (CMD_OK);
448
449         if (color_name_to_teken(value, &val)) {
450                 snprintf(buf, sizeof (buf), "%d", val);
451                 evalue = buf;
452         } else {
453                 char *end;
454
455                 errno = 0;
456                 val = (int)strtol(value, &end, 0);
457                 if (errno != 0 || *end != '\0') {
458                         printf("Allowed values are either ansi color name or "
459                             "number from range [0-7].\n");
460                         return (CMD_OK);
461                 }
462                 evalue = value;
463         }
464
465         ap = teken_get_defattr(&teken);
466         a = *ap;
467         if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
468                 /* is it already set? */
469                 if (ap->ta_fgcolor == val)
470                         return (CMD_OK);
471                 a.ta_fgcolor = val;
472         }
473         if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
474                 /* is it already set? */
475                 if (ap->ta_bgcolor == val)
476                         return (CMD_OK);
477                 a.ta_bgcolor = val;
478         }
479         env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
480         teken_set_defattr(&teken, &a);
481         return (CMD_OK);
482 }
483
484 #ifdef TERM_EMU
485 /* Get cursor position. */
486 void
487 get_pos(int *x, int *y)
488 {
489         *x = conout->Mode->CursorColumn;
490         *y = conout->Mode->CursorRow;
491 }
492
493 /* Move cursor to x rows and y cols (0-based). */
494 void
495 curs_move(int *_x, int *_y, int x, int y)
496 {
497         conout->SetCursorPosition(conout, x, y);
498         if (_x != NULL)
499                 *_x = conout->Mode->CursorColumn;
500         if (_y != NULL)
501                 *_y = conout->Mode->CursorRow;
502 }
503  
504 /* Clear internal state of the terminal emulation code. */
505 void
506 end_term(void)
507 {
508         esc = 0;
509         argc = -1;
510 }
511 #endif
512
513 static void
514 efi_cons_rawputchar(int c)
515 {
516         int i;
517         UINTN x, y;
518         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
519
520         if (c == '\t') {
521                 int n;
522
523                 n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
524                 for (i = 0; i < n; i++)
525                         efi_cons_rawputchar(' ');
526         } else {
527 #ifndef TERM_EMU
528                 if (c == '\n')
529                         efi_cons_efiputchar('\r');
530                 efi_cons_efiputchar(c);
531 #else
532                 switch (c) {
533                 case '\r':
534                         curx = 0;
535                         efi_cons_efiputchar('\r');
536                         return;
537                 case '\n':
538                         efi_cons_efiputchar('\n');
539                         efi_cons_efiputchar('\r');
540                         cury++;
541                         if (cury >= y)
542                                 cury--;
543                         curx = 0;
544                         return;
545                 case '\b':
546                         if (curx > 0) {
547                                 efi_cons_efiputchar('\b');
548                                 curx--;
549                         }
550                         return;
551                 default:
552                         efi_cons_efiputchar(c);
553                         curx++;
554                         if (curx > x-1) {
555                                 curx = 0;
556                                 cury++;
557                         }
558                         if (cury > y-1) {
559                                 curx = 0;
560                                 cury--;
561                         }
562                 }
563 #endif
564         }
565         conout->EnableCursor(conout, TRUE);
566 }
567
568 #ifdef TERM_EMU
569 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
570 static void
571 bail_out(int c)
572 {
573         char buf[16], *ch;
574         int i;
575
576         if (esc) {
577                 efi_cons_rawputchar('\033');
578                 if (esc != '\033')
579                         efi_cons_rawputchar(esc);
580                 for (i = 0; i <= argc; ++i) {
581                         sprintf(buf, "%d", args[i]);
582                         ch = buf;
583                         while (*ch)
584                                 efi_cons_rawputchar(*ch++);
585                 }
586         }
587         efi_cons_rawputchar(c);
588         end_term();
589 }
590
591 /* Clear display from current position to end of screen. */
592 static void
593 CD(void)
594 {
595         int i;
596         UINTN x, y;
597
598         get_pos(&curx, &cury);
599         if (curx == 0 && cury == 0) {
600                 conout->ClearScreen(conout);
601                 end_term();
602                 return;
603         }
604
605         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
606         CL(0);  /* clear current line from cursor to end */
607         for (i = cury + 1; i < y-1; i++) {
608                 curs_move(NULL, NULL, 0, i);
609                 CL(0);
610         }
611         curs_move(NULL, NULL, curx, cury);
612         end_term();
613 }
614
615 /*
616  * Absolute cursor move to args[0] rows and args[1] columns
617  * (the coordinates are 1-based).
618  */
619 static void
620 CM(void)
621 {
622         if (args[0] > 0)
623                 args[0]--;
624         if (args[1] > 0)
625                 args[1]--;
626         curs_move(&curx, &cury, args[1], args[0]);
627         end_term();
628 }
629
630 /* Home cursor (left top corner), also called from mode command. */
631 void
632 HO(void)
633 {
634         argc = 1;
635         args[0] = args[1] = 1;
636         CM();
637 }
638  
639 /* Clear line from current position to end of line */
640 static void
641 CL(int direction)
642 {
643         int i, len;
644         UINTN x, y;
645         CHAR16 *line;
646
647         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
648         switch (direction) {
649         case 0: /* from cursor to end */
650                 len = x - curx + 1;
651                 break;
652         case 1: /* from beginning to cursor */
653                 len = curx;
654                 break;
655         case 2: /* entire line */
656                 len = x;
657                 break;
658         default:        /* NOTREACHED */
659                 __unreachable();
660         }
661  
662         if (cury == y - 1)
663                 len--;
664
665         line = malloc(len * sizeof (CHAR16));
666         if (line == NULL) {
667                 printf("out of memory\n");
668                 return;
669         }
670         for (i = 0; i < len; i++)
671                 line[i] = ' ';
672         line[len-1] = 0;
673
674         if (direction != 0)
675                 curs_move(NULL, NULL, 0, cury);
676  
677         conout->OutputString(conout, line);
678         /* restore cursor position */
679         curs_move(NULL, NULL, curx, cury);
680         free(line);
681         end_term();
682 }
683
684 static void
685 get_arg(int c)
686 {
687         if (argc < 0)
688                 argc = 0;
689         args[argc] *= 10;
690         args[argc] += c - '0';
691 }
692 #endif
693  
694 /* Emulate basic capabilities of cons25 terminal */
695 static void
696 efi_term_emu(int c)
697 {
698 #ifdef TERM_EMU
699         static int ansi_col[] = {
700                 0, 4, 2, 6, 1, 5, 3, 7
701         };
702         int t, i;
703         EFI_STATUS status;
704  
705         switch (esc) {
706         case 0:
707                 switch (c) {
708                 case '\033':
709                         esc = c;
710                         break;
711                 default:
712                         efi_cons_rawputchar(c);
713                         break;
714                 }
715                 break;
716         case '\033':
717                 switch (c) {
718                 case '[':
719                         esc = c;
720                         args[0] = 0;
721                         argc = -1;
722                         break;
723                 default:
724                         bail_out(c);
725                         break;
726                 }
727                 break;
728         case '[':
729                 switch (c) {
730                 case ';':
731                         if (argc < 0)
732                                 argc = 0;
733                         else if (argc + 1 >= MAXARGS)
734                                 bail_out(c);
735                         else
736                                 args[++argc] = 0;
737                         break;
738                 case 'H':               /* ho = \E[H */
739                         if (argc < 0)
740                                 HO();
741                         else if (argc == 1)
742                                 CM();
743                         else
744                                 bail_out(c);
745                         break;
746                 case 'J':               /* cd = \E[J */
747                         if (argc < 0)
748                                 CD();
749                         else
750                                 bail_out(c);
751                         break;
752                 case 'm':
753                         if (argc < 0) {
754                                 fg_c = DEFAULT_FGCOLOR;
755                                 bg_c = DEFAULT_BGCOLOR;
756                         }
757                         for (i = 0; i <= argc; ++i) {
758                                 switch (args[i]) {
759                                 case 0:         /* back to normal */
760                                         fg_c = DEFAULT_FGCOLOR;
761                                         bg_c = DEFAULT_BGCOLOR;
762                                         break;
763                                 case 1:         /* bold */
764                                         fg_c |= 0x8;
765                                         break;
766                                 case 4:         /* underline */
767                                 case 5:         /* blink */
768                                         bg_c |= 0x8;
769                                         break;
770                                 case 7:         /* reverse */
771                                         t = fg_c;
772                                         fg_c = bg_c;
773                                         bg_c = t;
774                                         break;
775                                 case 22:        /* normal intensity */
776                                         fg_c &= ~0x8;
777                                         break;
778                                 case 24:        /* not underline */
779                                 case 25:        /* not blinking */
780                                         bg_c &= ~0x8;
781                                         break;
782                                 case 30: case 31: case 32: case 33:
783                                 case 34: case 35: case 36: case 37:
784                                         fg_c = ansi_col[args[i] - 30];
785                                         break;
786                                 case 39:        /* normal */
787                                         fg_c = DEFAULT_FGCOLOR;
788                                         break;
789                                 case 40: case 41: case 42: case 43:
790                                 case 44: case 45: case 46: case 47:
791                                         bg_c = ansi_col[args[i] - 40];
792                                         break;
793                                 case 49:        /* normal */
794                                         bg_c = DEFAULT_BGCOLOR;
795                                         break;
796                                 }
797                         }
798                         conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
799                         end_term();
800                         break;
801                 default:
802                         if (isdigit(c))
803                                 get_arg(c);
804                         else
805                                 bail_out(c);
806                         break;
807                 }
808                 break;
809         default:
810                 bail_out(c);
811                 break;
812         }
813 #else
814         efi_cons_rawputchar(c);
815 #endif
816 }
817
818 bool
819 efi_cons_update_mode(void)
820 {
821         UINTN cols, rows;
822         const teken_attr_t *a;
823         EFI_STATUS status;
824         char env[8];
825
826         status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
827         if (EFI_ERROR(status)) {
828                 cols = 80;
829                 rows = 24;
830         }
831
832         /*
833          * When we have serial port listed in ConOut, use pre-teken emulator,
834          * if built with.
835          * The problem is, we can not output text on efi and comconsole when
836          * efi also has comconsole bound. But then again, we need to have
837          * terminal emulator for efi text mode to support the menu.
838          * While teken is too expensive to be used on serial console, the
839          * pre-teken emulator is light enough to be used on serial console.
840          *
841          * When doing multiple consoles (both serial and video),
842          * also just use the old emulator. RB_MULTIPLE also implies
843          * we're using a serial console.
844          */
845         mode = parse_uefi_con_out();
846         if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
847                 if (buffer != NULL) {
848                         if (tp.tp_row == rows && tp.tp_col == cols)
849                                 return (true);
850                         free(buffer);
851                 } else {
852                         teken_init(&teken, &tf, NULL);
853                 }
854
855                 tp.tp_row = rows;
856                 tp.tp_col = cols;
857                 buffer = malloc(rows * cols * sizeof(*buffer));
858                 if (buffer == NULL)
859                         return (false);
860
861                 teken_set_winsize(&teken, &tp);
862                 a = teken_get_defattr(&teken);
863
864                 snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
865                 env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors,
866                     env_nounset);
867                 snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
868                 env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors,
869                     env_nounset);
870
871                 for (int row = 0; row < rows; row++) {
872                         for (int col = 0; col < cols; col++) {
873                                 buffer[col + row * tp.tp_col].c = ' ';
874                                 buffer[col + row * tp.tp_col].a = *a;
875                         }
876                 }
877         } else {
878 #ifdef TERM_EMU
879                 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
880                     DEFAULT_BGCOLOR));
881                 end_term();
882                 get_pos(&curx, &cury);
883                 curs_move(&curx, &cury, curx, cury);
884                 fg_c = DEFAULT_FGCOLOR;
885                 bg_c = DEFAULT_BGCOLOR;
886 #endif
887         }
888
889         snprintf(env, sizeof (env), "%u", (unsigned)rows);
890         setenv("LINES", env, 1);
891         snprintf(env, sizeof (env), "%u", (unsigned)cols);
892         setenv("COLUMNS", env, 1);
893
894         return (true);
895 }
896
897 static int
898 efi_cons_init(int arg)
899 {
900         EFI_STATUS status;
901
902         if (conin != NULL)
903                 return (0);
904
905         conout->EnableCursor(conout, TRUE);
906         if (efi_cons_update_mode())
907                 return (0);
908
909         return (1);
910 }
911
912 static void
913 input_partial(void)
914 {
915         unsigned i;
916         uint32_t c;
917
918         if (utf8_left == 0)
919                 return;
920
921         for (i = 0; i < sizeof(utf8_partial); i++) {
922                 c = (utf8_partial >> (24 - (i << 3))) & 0xff;
923                 if (c != 0)
924                         efi_term_emu(c);
925         }
926         utf8_left = 0;
927         utf8_partial = 0;
928 }
929
930 static void
931 input_byte(uint8_t c)
932 {
933         if ((c & 0x80) == 0x00) {
934                 /* One-byte sequence. */
935                 input_partial();
936                 efi_term_emu(c);
937                 return;
938         }
939         if ((c & 0xe0) == 0xc0) {
940                 /* Two-byte sequence. */
941                 input_partial();
942                 utf8_left = 1;
943                 utf8_partial = c;
944                 return;
945         }
946         if ((c & 0xf0) == 0xe0) {
947                 /* Three-byte sequence. */
948                 input_partial();
949                 utf8_left = 2;
950                 utf8_partial = c;
951                 return;
952         }
953         if ((c & 0xf8) == 0xf0) {
954                 /* Four-byte sequence. */
955                 input_partial();
956                 utf8_left = 3;
957                 utf8_partial = c;
958                 return;
959         }
960         if ((c & 0xc0) == 0x80) {
961                 /* Invalid state? */
962                 if (utf8_left == 0) {
963                         efi_term_emu(c);
964                         return;
965                 }
966                 utf8_left--;
967                 utf8_partial = (utf8_partial << 8) | c;
968                 if (utf8_left == 0) {
969                         uint32_t v, u;
970                         uint8_t b;
971
972                         v = 0;
973                         u = utf8_partial;
974                         b = (u >> 24) & 0xff;
975                         if (b != 0) {           /* Four-byte sequence */
976                                 v = b & 0x07;
977                                 b = (u >> 16) & 0xff;
978                                 v = (v << 6) | (b & 0x3f);
979                                 b = (u >> 8) & 0xff;
980                                 v = (v << 6) | (b & 0x3f);
981                                 b = u & 0xff;
982                                 v = (v << 6) | (b & 0x3f);
983                         } else if ((b = (u >> 16) & 0xff) != 0) {
984                                 v = b & 0x0f;   /* Three-byte sequence */
985                                 b = (u >> 8) & 0xff;
986                                 v = (v << 6) | (b & 0x3f);
987                                 b = u & 0xff;
988                                 v = (v << 6) | (b & 0x3f);
989                         } else if ((b = (u >> 8) & 0xff) != 0) {
990                                 v = b & 0x1f;   /* Two-byte sequence */
991                                 b = u & 0xff;
992                                 v = (v << 6) | (b & 0x3f);
993                         }
994                         /* Send unicode char directly to console. */
995                         efi_cons_efiputchar(v);
996                         utf8_partial = 0;
997                 }
998                 return;
999         }
1000         /* Anything left is illegal in UTF-8 sequence. */
1001         input_partial();
1002         efi_term_emu(c);
1003 }
1004
1005 void
1006 efi_cons_putchar(int c)
1007 {
1008         unsigned char ch = c;
1009
1010         /*
1011          * Don't use Teken when we're doing pure serial, or a multiple console
1012          * with video "primary" because that's also serial.
1013          */
1014         if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0) {
1015                 input_byte(ch);
1016                 return;
1017         }
1018
1019         if (buffer != NULL)
1020                 teken_input(&teken, &ch, sizeof (ch));
1021         else
1022                 efi_cons_efiputchar(c);
1023 }
1024
1025 static int
1026 keybuf_getchar(void)
1027 {
1028         int i, c = 0;
1029
1030         for (i = 0; i < KEYBUFSZ; i++) {
1031                 if (keybuf[i] != 0) {
1032                         c = keybuf[i];
1033                         keybuf[i] = 0;
1034                         break;
1035                 }
1036         }
1037
1038         return (c);
1039 }
1040
1041 static bool
1042 keybuf_ischar(void)
1043 {
1044         int i;
1045
1046         for (i = 0; i < KEYBUFSZ; i++) {
1047                 if (keybuf[i] != 0)
1048                         return (true);
1049         }
1050         return (false);
1051 }
1052
1053 /*
1054  * We are not reading input before keybuf is empty, so we are safe
1055  * just to fill keybuf from the beginning.
1056  */
1057 static void
1058 keybuf_inschar(EFI_INPUT_KEY *key)
1059 {
1060
1061         switch (key->ScanCode) {
1062         case SCAN_UP: /* UP */
1063                 keybuf[0] = 0x1b;       /* esc */
1064                 keybuf[1] = '[';
1065                 keybuf[2] = 'A';
1066                 break;
1067         case SCAN_DOWN: /* DOWN */
1068                 keybuf[0] = 0x1b;       /* esc */
1069                 keybuf[1] = '[';
1070                 keybuf[2] = 'B';
1071                 break;
1072         case SCAN_RIGHT: /* RIGHT */
1073                 keybuf[0] = 0x1b;       /* esc */
1074                 keybuf[1] = '[';
1075                 keybuf[2] = 'C';
1076                 break;
1077         case SCAN_LEFT: /* LEFT */
1078                 keybuf[0] = 0x1b;       /* esc */
1079                 keybuf[1] = '[';
1080                 keybuf[2] = 'D';
1081                 break;
1082         case SCAN_DELETE:
1083                 keybuf[0] = CHAR_BACKSPACE;
1084                 break;
1085         case SCAN_ESC:
1086                 keybuf[0] = 0x1b;       /* esc */
1087                 break;
1088         default:
1089                 keybuf[0] = key->UnicodeChar;
1090                 break;
1091         }
1092 }
1093
1094 static bool
1095 efi_readkey(void)
1096 {
1097         EFI_STATUS status;
1098         EFI_INPUT_KEY key;
1099
1100         status = conin->ReadKeyStroke(conin, &key);
1101         if (status == EFI_SUCCESS) {
1102                 keybuf_inschar(&key);
1103                 return (true);
1104         }
1105         return (false);
1106 }
1107
1108 static bool
1109 efi_readkey_ex(void)
1110 {
1111         EFI_STATUS status;
1112         EFI_INPUT_KEY *kp;
1113         EFI_KEY_DATA  key_data;
1114         uint32_t kss;
1115
1116         status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1117         if (status == EFI_SUCCESS) {
1118                 kss = key_data.KeyState.KeyShiftState;
1119                 kp = &key_data.Key;
1120                 if (kss & EFI_SHIFT_STATE_VALID) {
1121
1122                         /*
1123                          * quick mapping to control chars, replace with
1124                          * map lookup later.
1125                          */
1126                         if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1127                             kss & EFI_LEFT_CONTROL_PRESSED) {
1128                                 if (kp->UnicodeChar >= 'a' &&
1129                                     kp->UnicodeChar <= 'z') {
1130                                         kp->UnicodeChar -= 'a';
1131                                         kp->UnicodeChar++;
1132                                 }
1133                         }
1134                 }
1135                 /*
1136                  * The shift state and/or toggle state may not be valid,
1137                  * but we still can have ScanCode or UnicodeChar.
1138                  */
1139                 if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1140                         return (false);
1141                 keybuf_inschar(kp);
1142                 return (true);
1143         }
1144         return (false);
1145 }
1146
1147 int
1148 efi_cons_getchar(void)
1149 {
1150         int c;
1151
1152         if ((c = keybuf_getchar()) != 0)
1153                 return (c);
1154
1155         key_pending = 0;
1156
1157         if (coninex == NULL) {
1158                 if (efi_readkey())
1159                         return (keybuf_getchar());
1160         } else {
1161                 if (efi_readkey_ex())
1162                         return (keybuf_getchar());
1163         }
1164
1165         return (-1);
1166 }
1167
1168 int
1169 efi_cons_poll(void)
1170 {
1171         EFI_STATUS status;
1172
1173         if (keybuf_ischar() || key_pending)
1174                 return (1);
1175
1176         /*
1177          * Some EFI implementation (u-boot for example) do not support
1178          * WaitForKey().
1179          * CheckEvent() can clear the signaled state.
1180          */
1181         if (coninex != NULL) {
1182                 if (coninex->WaitForKeyEx == NULL) {
1183                         key_pending = efi_readkey_ex();
1184                 } else {
1185                         status = BS->CheckEvent(coninex->WaitForKeyEx);
1186                         key_pending = status == EFI_SUCCESS;
1187                 }
1188         } else {
1189                 if (conin->WaitForKey == NULL) {
1190                         key_pending = efi_readkey();
1191                 } else {
1192                         status = BS->CheckEvent(conin->WaitForKey);
1193                         key_pending = status == EFI_SUCCESS;
1194                 }
1195         }
1196
1197         return (key_pending);
1198 }
1199
1200 /* Plain direct access to EFI OutputString(). */
1201 void
1202 efi_cons_efiputchar(int c)
1203 {
1204         CHAR16 buf[2];
1205         EFI_STATUS status;
1206
1207         buf[0] = c;
1208         buf[1] = 0;     /* terminate string */
1209
1210         status = conout->TestString(conout, buf);
1211         if (EFI_ERROR(status))
1212                 buf[0] = '?';
1213         conout->OutputString(conout, buf);
1214 }