]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efi_console.c
loader.efi: handle efi_cons_init() failure cases better
[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                         teken_set_winsize(&teken, &tp);
860                         a = teken_get_defattr(&teken);
861
862                         snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
863                         env_setenv("teken.fg_color", EV_VOLATILE, env,
864                             efi_set_colors, env_nounset);
865                         snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
866                         env_setenv("teken.bg_color", EV_VOLATILE, env,
867                             efi_set_colors, env_nounset);
868
869                         for (int row = 0; row < rows; row++) {
870                                 for (int col = 0; col < cols; col++) {
871                                         buffer[col + row * tp.tp_col].c = ' ';
872                                         buffer[col + row * tp.tp_col].a = *a;
873                                 }
874                         }
875                 }
876         }
877
878 #ifdef TERM_EMU
879         if (buffer == NULL) {
880                 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
881                     DEFAULT_BGCOLOR));
882                 end_term();
883                 get_pos(&curx, &cury);
884                 curs_move(&curx, &cury, curx, cury);
885                 fg_c = DEFAULT_FGCOLOR;
886                 bg_c = DEFAULT_BGCOLOR;
887         }
888 #endif
889
890         snprintf(env, sizeof (env), "%u", (unsigned)rows);
891         setenv("LINES", env, 1);
892         snprintf(env, sizeof (env), "%u", (unsigned)cols);
893         setenv("COLUMNS", env, 1);
894
895         return (true);
896 }
897
898 static int
899 efi_cons_init(int arg)
900 {
901         EFI_STATUS status;
902
903         if (conin != NULL)
904                 return (0);
905
906         conout->EnableCursor(conout, TRUE);
907         if (efi_cons_update_mode())
908                 return (0);
909
910         return (1);
911 }
912
913 static void
914 input_partial(void)
915 {
916         unsigned i;
917         uint32_t c;
918
919         if (utf8_left == 0)
920                 return;
921
922         for (i = 0; i < sizeof(utf8_partial); i++) {
923                 c = (utf8_partial >> (24 - (i << 3))) & 0xff;
924                 if (c != 0)
925                         efi_term_emu(c);
926         }
927         utf8_left = 0;
928         utf8_partial = 0;
929 }
930
931 static void
932 input_byte(uint8_t c)
933 {
934         if ((c & 0x80) == 0x00) {
935                 /* One-byte sequence. */
936                 input_partial();
937                 efi_term_emu(c);
938                 return;
939         }
940         if ((c & 0xe0) == 0xc0) {
941                 /* Two-byte sequence. */
942                 input_partial();
943                 utf8_left = 1;
944                 utf8_partial = c;
945                 return;
946         }
947         if ((c & 0xf0) == 0xe0) {
948                 /* Three-byte sequence. */
949                 input_partial();
950                 utf8_left = 2;
951                 utf8_partial = c;
952                 return;
953         }
954         if ((c & 0xf8) == 0xf0) {
955                 /* Four-byte sequence. */
956                 input_partial();
957                 utf8_left = 3;
958                 utf8_partial = c;
959                 return;
960         }
961         if ((c & 0xc0) == 0x80) {
962                 /* Invalid state? */
963                 if (utf8_left == 0) {
964                         efi_term_emu(c);
965                         return;
966                 }
967                 utf8_left--;
968                 utf8_partial = (utf8_partial << 8) | c;
969                 if (utf8_left == 0) {
970                         uint32_t v, u;
971                         uint8_t b;
972
973                         v = 0;
974                         u = utf8_partial;
975                         b = (u >> 24) & 0xff;
976                         if (b != 0) {           /* Four-byte sequence */
977                                 v = b & 0x07;
978                                 b = (u >> 16) & 0xff;
979                                 v = (v << 6) | (b & 0x3f);
980                                 b = (u >> 8) & 0xff;
981                                 v = (v << 6) | (b & 0x3f);
982                                 b = u & 0xff;
983                                 v = (v << 6) | (b & 0x3f);
984                         } else if ((b = (u >> 16) & 0xff) != 0) {
985                                 v = b & 0x0f;   /* Three-byte sequence */
986                                 b = (u >> 8) & 0xff;
987                                 v = (v << 6) | (b & 0x3f);
988                                 b = u & 0xff;
989                                 v = (v << 6) | (b & 0x3f);
990                         } else if ((b = (u >> 8) & 0xff) != 0) {
991                                 v = b & 0x1f;   /* Two-byte sequence */
992                                 b = u & 0xff;
993                                 v = (v << 6) | (b & 0x3f);
994                         }
995                         /* Send unicode char directly to console. */
996                         efi_cons_efiputchar(v);
997                         utf8_partial = 0;
998                 }
999                 return;
1000         }
1001         /* Anything left is illegal in UTF-8 sequence. */
1002         input_partial();
1003         efi_term_emu(c);
1004 }
1005
1006 void
1007 efi_cons_putchar(int c)
1008 {
1009         unsigned char ch = c;
1010
1011         /*
1012          * Don't use Teken when we're doing pure serial, or a multiple console
1013          * with video "primary" because that's also serial.
1014          */
1015         if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || buffer == NULL) {
1016                 input_byte(ch);
1017                 return;
1018         }
1019
1020         teken_input(&teken, &ch, sizeof (ch));
1021 }
1022
1023 static int
1024 keybuf_getchar(void)
1025 {
1026         int i, c = 0;
1027
1028         for (i = 0; i < KEYBUFSZ; i++) {
1029                 if (keybuf[i] != 0) {
1030                         c = keybuf[i];
1031                         keybuf[i] = 0;
1032                         break;
1033                 }
1034         }
1035
1036         return (c);
1037 }
1038
1039 static bool
1040 keybuf_ischar(void)
1041 {
1042         int i;
1043
1044         for (i = 0; i < KEYBUFSZ; i++) {
1045                 if (keybuf[i] != 0)
1046                         return (true);
1047         }
1048         return (false);
1049 }
1050
1051 /*
1052  * We are not reading input before keybuf is empty, so we are safe
1053  * just to fill keybuf from the beginning.
1054  */
1055 static void
1056 keybuf_inschar(EFI_INPUT_KEY *key)
1057 {
1058
1059         switch (key->ScanCode) {
1060         case SCAN_UP: /* UP */
1061                 keybuf[0] = 0x1b;       /* esc */
1062                 keybuf[1] = '[';
1063                 keybuf[2] = 'A';
1064                 break;
1065         case SCAN_DOWN: /* DOWN */
1066                 keybuf[0] = 0x1b;       /* esc */
1067                 keybuf[1] = '[';
1068                 keybuf[2] = 'B';
1069                 break;
1070         case SCAN_RIGHT: /* RIGHT */
1071                 keybuf[0] = 0x1b;       /* esc */
1072                 keybuf[1] = '[';
1073                 keybuf[2] = 'C';
1074                 break;
1075         case SCAN_LEFT: /* LEFT */
1076                 keybuf[0] = 0x1b;       /* esc */
1077                 keybuf[1] = '[';
1078                 keybuf[2] = 'D';
1079                 break;
1080         case SCAN_DELETE:
1081                 keybuf[0] = CHAR_BACKSPACE;
1082                 break;
1083         case SCAN_ESC:
1084                 keybuf[0] = 0x1b;       /* esc */
1085                 break;
1086         default:
1087                 keybuf[0] = key->UnicodeChar;
1088                 break;
1089         }
1090 }
1091
1092 static bool
1093 efi_readkey(void)
1094 {
1095         EFI_STATUS status;
1096         EFI_INPUT_KEY key;
1097
1098         status = conin->ReadKeyStroke(conin, &key);
1099         if (status == EFI_SUCCESS) {
1100                 keybuf_inschar(&key);
1101                 return (true);
1102         }
1103         return (false);
1104 }
1105
1106 static bool
1107 efi_readkey_ex(void)
1108 {
1109         EFI_STATUS status;
1110         EFI_INPUT_KEY *kp;
1111         EFI_KEY_DATA  key_data;
1112         uint32_t kss;
1113
1114         status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1115         if (status == EFI_SUCCESS) {
1116                 kss = key_data.KeyState.KeyShiftState;
1117                 kp = &key_data.Key;
1118                 if (kss & EFI_SHIFT_STATE_VALID) {
1119
1120                         /*
1121                          * quick mapping to control chars, replace with
1122                          * map lookup later.
1123                          */
1124                         if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1125                             kss & EFI_LEFT_CONTROL_PRESSED) {
1126                                 if (kp->UnicodeChar >= 'a' &&
1127                                     kp->UnicodeChar <= 'z') {
1128                                         kp->UnicodeChar -= 'a';
1129                                         kp->UnicodeChar++;
1130                                 }
1131                         }
1132                 }
1133                 /*
1134                  * The shift state and/or toggle state may not be valid,
1135                  * but we still can have ScanCode or UnicodeChar.
1136                  */
1137                 if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1138                         return (false);
1139                 keybuf_inschar(kp);
1140                 return (true);
1141         }
1142         return (false);
1143 }
1144
1145 int
1146 efi_cons_getchar(void)
1147 {
1148         int c;
1149
1150         if ((c = keybuf_getchar()) != 0)
1151                 return (c);
1152
1153         key_pending = 0;
1154
1155         if (coninex == NULL) {
1156                 if (efi_readkey())
1157                         return (keybuf_getchar());
1158         } else {
1159                 if (efi_readkey_ex())
1160                         return (keybuf_getchar());
1161         }
1162
1163         return (-1);
1164 }
1165
1166 int
1167 efi_cons_poll(void)
1168 {
1169         EFI_STATUS status;
1170
1171         if (keybuf_ischar() || key_pending)
1172                 return (1);
1173
1174         /*
1175          * Some EFI implementation (u-boot for example) do not support
1176          * WaitForKey().
1177          * CheckEvent() can clear the signaled state.
1178          */
1179         if (coninex != NULL) {
1180                 if (coninex->WaitForKeyEx == NULL) {
1181                         key_pending = efi_readkey_ex();
1182                 } else {
1183                         status = BS->CheckEvent(coninex->WaitForKeyEx);
1184                         key_pending = status == EFI_SUCCESS;
1185                 }
1186         } else {
1187                 if (conin->WaitForKey == NULL) {
1188                         key_pending = efi_readkey();
1189                 } else {
1190                         status = BS->CheckEvent(conin->WaitForKey);
1191                         key_pending = status == EFI_SUCCESS;
1192                 }
1193         }
1194
1195         return (key_pending);
1196 }
1197
1198 /* Plain direct access to EFI OutputString(). */
1199 void
1200 efi_cons_efiputchar(int c)
1201 {
1202         CHAR16 buf[2];
1203         EFI_STATUS status;
1204
1205         buf[0] = c;
1206         buf[1] = 0;     /* terminate string */
1207
1208         status = conout->TestString(conout, buf);
1209         if (EFI_ERROR(status))
1210                 buf[0] = '?';
1211         conout->OutputString(conout, buf);
1212 }