]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efi_console.c
MFV r357687: Import NFS fix for O_SEARCH tests
[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 static void
381 efi_cons_probe(struct console *cp)
382 {
383         cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
384 }
385
386 static bool
387 color_name_to_teken(const char *name, int *val)
388 {
389         if (strcasecmp(name, "black") == 0) {
390                 *val = TC_BLACK;
391                 return (true);
392         }
393         if (strcasecmp(name, "red") == 0) {
394                 *val = TC_RED;
395                 return (true);
396         }
397         if (strcasecmp(name, "green") == 0) {
398                 *val = TC_GREEN;
399                 return (true);
400         }
401         if (strcasecmp(name, "brown") == 0) {
402                 *val = TC_BROWN;
403                 return (true);
404         }
405         if (strcasecmp(name, "blue") == 0) {
406                 *val = TC_BLUE;
407                 return (true);
408         }
409         if (strcasecmp(name, "magenta") == 0) {
410                 *val = TC_MAGENTA;
411                 return (true);
412         }
413         if (strcasecmp(name, "cyan") == 0) {
414                 *val = TC_CYAN;
415                 return (true);
416         }
417         if (strcasecmp(name, "white") == 0) {
418                 *val = TC_WHITE;
419                 return (true);
420         }
421         return (false);
422 }
423
424 static int
425 efi_set_colors(struct env_var *ev, int flags, const void *value)
426 {
427         int val = 0;
428         char buf[2];
429         const void *evalue;
430         const teken_attr_t *ap;
431         teken_attr_t a;
432
433         if (value == NULL)
434                 return (CMD_OK);
435
436         if (color_name_to_teken(value, &val)) {
437                 snprintf(buf, sizeof (buf), "%d", val);
438                 evalue = buf;
439         } else {
440                 char *end;
441
442                 errno = 0;
443                 val = (int)strtol(value, &end, 0);
444                 if (errno != 0 || *end != '\0') {
445                         printf("Allowed values are either ansi color name or "
446                             "number from range [0-7].\n");
447                         return (CMD_OK);
448                 }
449                 evalue = value;
450         }
451
452         ap = teken_get_defattr(&teken);
453         a = *ap;
454         if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
455                 /* is it already set? */
456                 if (ap->ta_fgcolor == val)
457                         return (CMD_OK);
458                 a.ta_fgcolor = val;
459         }
460         if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
461                 /* is it already set? */
462                 if (ap->ta_bgcolor == val)
463                         return (CMD_OK);
464                 a.ta_bgcolor = val;
465         }
466         env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
467         teken_set_defattr(&teken, &a);
468         return (CMD_OK);
469 }
470
471 #ifdef TERM_EMU
472 /* Get cursor position. */
473 void
474 get_pos(int *x, int *y)
475 {
476         *x = conout->Mode->CursorColumn;
477         *y = conout->Mode->CursorRow;
478 }
479
480 /* Move cursor to x rows and y cols (0-based). */
481 void
482 curs_move(int *_x, int *_y, int x, int y)
483 {
484         conout->SetCursorPosition(conout, x, y);
485         if (_x != NULL)
486                 *_x = conout->Mode->CursorColumn;
487         if (_y != NULL)
488                 *_y = conout->Mode->CursorRow;
489 }
490  
491 /* Clear internal state of the terminal emulation code. */
492 void
493 end_term(void)
494 {
495         esc = 0;
496         argc = -1;
497 }
498 #endif
499
500 static void
501 efi_cons_rawputchar(int c)
502 {
503         int i;
504         UINTN x, y;
505         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
506
507         if (c == '\t') {
508                 int n;
509
510                 n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
511                 for (i = 0; i < n; i++)
512                         efi_cons_rawputchar(' ');
513         } else {
514 #ifndef TERM_EMU
515                 if (c == '\n')
516                         efi_cons_efiputchar('\r');
517                 efi_cons_efiputchar(c);
518 #else
519                 switch (c) {
520                 case '\r':
521                         curx = 0;
522                         efi_cons_efiputchar('\r');
523                         return;
524                 case '\n':
525                         efi_cons_efiputchar('\n');
526                         efi_cons_efiputchar('\r');
527                         cury++;
528                         if (cury >= y)
529                                 cury--;
530                         curx = 0;
531                         return;
532                 case '\b':
533                         if (curx > 0) {
534                                 efi_cons_efiputchar('\b');
535                                 curx--;
536                         }
537                         return;
538                 default:
539                         efi_cons_efiputchar(c);
540                         curx++;
541                         if (curx > x-1) {
542                                 curx = 0;
543                                 cury++;
544                         }
545                         if (cury > y-1) {
546                                 curx = 0;
547                                 cury--;
548                         }
549                 }
550 #endif
551         }
552         conout->EnableCursor(conout, TRUE);
553 }
554
555 #ifdef TERM_EMU
556 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
557 static void
558 bail_out(int c)
559 {
560         char buf[16], *ch;
561         int i;
562
563         if (esc) {
564                 efi_cons_rawputchar('\033');
565                 if (esc != '\033')
566                         efi_cons_rawputchar(esc);
567                 for (i = 0; i <= argc; ++i) {
568                         sprintf(buf, "%d", args[i]);
569                         ch = buf;
570                         while (*ch)
571                                 efi_cons_rawputchar(*ch++);
572                 }
573         }
574         efi_cons_rawputchar(c);
575         end_term();
576 }
577
578 /* Clear display from current position to end of screen. */
579 static void
580 CD(void)
581 {
582         int i;
583         UINTN x, y;
584
585         get_pos(&curx, &cury);
586         if (curx == 0 && cury == 0) {
587                 conout->ClearScreen(conout);
588                 end_term();
589                 return;
590         }
591
592         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
593         CL(0);  /* clear current line from cursor to end */
594         for (i = cury + 1; i < y-1; i++) {
595                 curs_move(NULL, NULL, 0, i);
596                 CL(0);
597         }
598         curs_move(NULL, NULL, curx, cury);
599         end_term();
600 }
601
602 /*
603  * Absolute cursor move to args[0] rows and args[1] columns
604  * (the coordinates are 1-based).
605  */
606 static void
607 CM(void)
608 {
609         if (args[0] > 0)
610                 args[0]--;
611         if (args[1] > 0)
612                 args[1]--;
613         curs_move(&curx, &cury, args[1], args[0]);
614         end_term();
615 }
616
617 /* Home cursor (left top corner), also called from mode command. */
618 void
619 HO(void)
620 {
621         argc = 1;
622         args[0] = args[1] = 1;
623         CM();
624 }
625  
626 /* Clear line from current position to end of line */
627 static void
628 CL(int direction)
629 {
630         int i, len;
631         UINTN x, y;
632         CHAR16 *line;
633
634         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
635         switch (direction) {
636         case 0: /* from cursor to end */
637                 len = x - curx + 1;
638                 break;
639         case 1: /* from beginning to cursor */
640                 len = curx;
641                 break;
642         case 2: /* entire line */
643                 len = x;
644                 break;
645         default:        /* NOTREACHED */
646                 __unreachable();
647         }
648  
649         if (cury == y - 1)
650                 len--;
651
652         line = malloc(len * sizeof (CHAR16));
653         if (line == NULL) {
654                 printf("out of memory\n");
655                 return;
656         }
657         for (i = 0; i < len; i++)
658                 line[i] = ' ';
659         line[len-1] = 0;
660
661         if (direction != 0)
662                 curs_move(NULL, NULL, 0, cury);
663  
664         conout->OutputString(conout, line);
665         /* restore cursor position */
666         curs_move(NULL, NULL, curx, cury);
667         free(line);
668         end_term();
669 }
670
671 static void
672 get_arg(int c)
673 {
674         if (argc < 0)
675                 argc = 0;
676         args[argc] *= 10;
677         args[argc] += c - '0';
678 }
679 #endif
680  
681 /* Emulate basic capabilities of cons25 terminal */
682 static void
683 efi_term_emu(int c)
684 {
685 #ifdef TERM_EMU
686         static int ansi_col[] = {
687                 0, 4, 2, 6, 1, 5, 3, 7
688         };
689         int t, i;
690         EFI_STATUS status;
691  
692         switch (esc) {
693         case 0:
694                 switch (c) {
695                 case '\033':
696                         esc = c;
697                         break;
698                 default:
699                         efi_cons_rawputchar(c);
700                         break;
701                 }
702                 break;
703         case '\033':
704                 switch (c) {
705                 case '[':
706                         esc = c;
707                         args[0] = 0;
708                         argc = -1;
709                         break;
710                 default:
711                         bail_out(c);
712                         break;
713                 }
714                 break;
715         case '[':
716                 switch (c) {
717                 case ';':
718                         if (argc < 0)
719                                 argc = 0;
720                         else if (argc + 1 >= MAXARGS)
721                                 bail_out(c);
722                         else
723                                 args[++argc] = 0;
724                         break;
725                 case 'H':               /* ho = \E[H */
726                         if (argc < 0)
727                                 HO();
728                         else if (argc == 1)
729                                 CM();
730                         else
731                                 bail_out(c);
732                         break;
733                 case 'J':               /* cd = \E[J */
734                         if (argc < 0)
735                                 CD();
736                         else
737                                 bail_out(c);
738                         break;
739                 case 'm':
740                         if (argc < 0) {
741                                 fg_c = DEFAULT_FGCOLOR;
742                                 bg_c = DEFAULT_BGCOLOR;
743                         }
744                         for (i = 0; i <= argc; ++i) {
745                                 switch (args[i]) {
746                                 case 0:         /* back to normal */
747                                         fg_c = DEFAULT_FGCOLOR;
748                                         bg_c = DEFAULT_BGCOLOR;
749                                         break;
750                                 case 1:         /* bold */
751                                         fg_c |= 0x8;
752                                         break;
753                                 case 4:         /* underline */
754                                 case 5:         /* blink */
755                                         bg_c |= 0x8;
756                                         break;
757                                 case 7:         /* reverse */
758                                         t = fg_c;
759                                         fg_c = bg_c;
760                                         bg_c = t;
761                                         break;
762                                 case 22:        /* normal intensity */
763                                         fg_c &= ~0x8;
764                                         break;
765                                 case 24:        /* not underline */
766                                 case 25:        /* not blinking */
767                                         bg_c &= ~0x8;
768                                         break;
769                                 case 30: case 31: case 32: case 33:
770                                 case 34: case 35: case 36: case 37:
771                                         fg_c = ansi_col[args[i] - 30];
772                                         break;
773                                 case 39:        /* normal */
774                                         fg_c = DEFAULT_FGCOLOR;
775                                         break;
776                                 case 40: case 41: case 42: case 43:
777                                 case 44: case 45: case 46: case 47:
778                                         bg_c = ansi_col[args[i] - 40];
779                                         break;
780                                 case 49:        /* normal */
781                                         bg_c = DEFAULT_BGCOLOR;
782                                         break;
783                                 }
784                         }
785                         conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
786                         end_term();
787                         break;
788                 default:
789                         if (isdigit(c))
790                                 get_arg(c);
791                         else
792                                 bail_out(c);
793                         break;
794                 }
795                 break;
796         default:
797                 bail_out(c);
798                 break;
799         }
800 #else
801         efi_cons_rawputchar(c);
802 #endif
803 }
804
805 bool
806 efi_cons_update_mode(void)
807 {
808         UINTN cols, rows;
809         const teken_attr_t *a;
810         EFI_STATUS status;
811         char env[8];
812
813         status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
814         if (EFI_ERROR(status)) {
815                 cols = 80;
816                 rows = 24;
817         }
818
819         /*
820          * When we have serial port listed in ConOut, use pre-teken emulator,
821          * if built with.
822          * The problem is, we can not output text on efi and comconsole when
823          * efi also has comconsole bound. But then again, we need to have
824          * terminal emulator for efi text mode to support the menu.
825          * While teken is too expensive to be used on serial console, the
826          * pre-teken emulator is light enough to be used on serial console.
827          *
828          * When doing multiple consoles (both serial and video),
829          * also just use the old emulator. RB_MULTIPLE also implies
830          * we're using a serial console.
831          */
832         mode = parse_uefi_con_out();
833         if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
834                 if (buffer != NULL) {
835                         if (tp.tp_row == rows && tp.tp_col == cols)
836                                 return (true);
837                         free(buffer);
838                 } else {
839                         teken_init(&teken, &tf, NULL);
840                 }
841
842                 tp.tp_row = rows;
843                 tp.tp_col = cols;
844                 buffer = malloc(rows * cols * sizeof(*buffer));
845                 if (buffer == NULL)
846                         return (false);
847
848                 teken_set_winsize(&teken, &tp);
849                 a = teken_get_defattr(&teken);
850
851                 snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
852                 env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors,
853                     env_nounset);
854                 snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
855                 env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors,
856                     env_nounset);
857
858                 for (int row = 0; row < rows; row++) {
859                         for (int col = 0; col < cols; col++) {
860                                 buffer[col + row * tp.tp_col].c = ' ';
861                                 buffer[col + row * tp.tp_col].a = *a;
862                         }
863                 }
864         } else {
865 #ifdef TERM_EMU
866                 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
867                     DEFAULT_BGCOLOR));
868                 end_term();
869                 get_pos(&curx, &cury);
870                 curs_move(&curx, &cury, curx, cury);
871                 fg_c = DEFAULT_FGCOLOR;
872                 bg_c = DEFAULT_BGCOLOR;
873 #endif
874         }
875
876         snprintf(env, sizeof (env), "%u", (unsigned)rows);
877         setenv("LINES", env, 1);
878         snprintf(env, sizeof (env), "%u", (unsigned)cols);
879         setenv("COLUMNS", env, 1);
880
881         return (true);
882 }
883
884 static int
885 efi_cons_init(int arg)
886 {
887         EFI_STATUS status;
888
889         if (conin != NULL)
890                 return (0);
891
892         conout = ST->ConOut;
893         conin = ST->ConIn;
894
895         conout->EnableCursor(conout, TRUE);
896         status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
897             (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
898         if (status != EFI_SUCCESS)
899                 coninex = NULL;
900
901         if (efi_cons_update_mode())
902                 return (0);
903
904         return (1);
905 }
906
907 static void
908 input_partial(void)
909 {
910         unsigned i;
911         uint32_t c;
912
913         if (utf8_left == 0)
914                 return;
915
916         for (i = 0; i < sizeof(utf8_partial); i++) {
917                 c = (utf8_partial >> (24 - (i << 3))) & 0xff;
918                 if (c != 0)
919                         efi_term_emu(c);
920         }
921         utf8_left = 0;
922         utf8_partial = 0;
923 }
924
925 static void
926 input_byte(uint8_t c)
927 {
928         if ((c & 0x80) == 0x00) {
929                 /* One-byte sequence. */
930                 input_partial();
931                 efi_term_emu(c);
932                 return;
933         }
934         if ((c & 0xe0) == 0xc0) {
935                 /* Two-byte sequence. */
936                 input_partial();
937                 utf8_left = 1;
938                 utf8_partial = c;
939                 return;
940         }
941         if ((c & 0xf0) == 0xe0) {
942                 /* Three-byte sequence. */
943                 input_partial();
944                 utf8_left = 2;
945                 utf8_partial = c;
946                 return;
947         }
948         if ((c & 0xf8) == 0xf0) {
949                 /* Four-byte sequence. */
950                 input_partial();
951                 utf8_left = 3;
952                 utf8_partial = c;
953                 return;
954         }
955         if ((c & 0xc0) == 0x80) {
956                 /* Invalid state? */
957                 if (utf8_left == 0) {
958                         efi_term_emu(c);
959                         return;
960                 }
961                 utf8_left--;
962                 utf8_partial = (utf8_partial << 8) | c;
963                 if (utf8_left == 0) {
964                         uint32_t v, u;
965                         uint8_t b;
966
967                         v = 0;
968                         u = utf8_partial;
969                         b = (u >> 24) & 0xff;
970                         if (b != 0) {           /* Four-byte sequence */
971                                 v = b & 0x07;
972                                 b = (u >> 16) & 0xff;
973                                 v = (v << 6) | (b & 0x3f);
974                                 b = (u >> 8) & 0xff;
975                                 v = (v << 6) | (b & 0x3f);
976                                 b = u & 0xff;
977                                 v = (v << 6) | (b & 0x3f);
978                         } else if ((b = (u >> 16) & 0xff) != 0) {
979                                 v = b & 0x0f;   /* Three-byte sequence */
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 >> 8) & 0xff) != 0) {
985                                 v = b & 0x1f;   /* Two-byte sequence */
986                                 b = u & 0xff;
987                                 v = (v << 6) | (b & 0x3f);
988                         }
989                         /* Send unicode char directly to console. */
990                         efi_cons_efiputchar(v);
991                         utf8_partial = 0;
992                 }
993                 return;
994         }
995         /* Anything left is illegal in UTF-8 sequence. */
996         input_partial();
997         efi_term_emu(c);
998 }
999
1000 void
1001 efi_cons_putchar(int c)
1002 {
1003         unsigned char ch = c;
1004
1005         /*
1006          * Don't use Teken when we're doing pure serial, or a multiple console
1007          * with video "primary" because that's also serial.
1008          */
1009         if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0) {
1010                 input_byte(ch);
1011                 return;
1012         }
1013
1014         if (buffer != NULL)
1015                 teken_input(&teken, &ch, sizeof (ch));
1016         else
1017                 efi_cons_efiputchar(c);
1018 }
1019
1020 static int
1021 keybuf_getchar(void)
1022 {
1023         int i, c = 0;
1024
1025         for (i = 0; i < KEYBUFSZ; i++) {
1026                 if (keybuf[i] != 0) {
1027                         c = keybuf[i];
1028                         keybuf[i] = 0;
1029                         break;
1030                 }
1031         }
1032
1033         return (c);
1034 }
1035
1036 static bool
1037 keybuf_ischar(void)
1038 {
1039         int i;
1040
1041         for (i = 0; i < KEYBUFSZ; i++) {
1042                 if (keybuf[i] != 0)
1043                         return (true);
1044         }
1045         return (false);
1046 }
1047
1048 /*
1049  * We are not reading input before keybuf is empty, so we are safe
1050  * just to fill keybuf from the beginning.
1051  */
1052 static void
1053 keybuf_inschar(EFI_INPUT_KEY *key)
1054 {
1055
1056         switch (key->ScanCode) {
1057         case SCAN_UP: /* UP */
1058                 keybuf[0] = 0x1b;       /* esc */
1059                 keybuf[1] = '[';
1060                 keybuf[2] = 'A';
1061                 break;
1062         case SCAN_DOWN: /* DOWN */
1063                 keybuf[0] = 0x1b;       /* esc */
1064                 keybuf[1] = '[';
1065                 keybuf[2] = 'B';
1066                 break;
1067         case SCAN_RIGHT: /* RIGHT */
1068                 keybuf[0] = 0x1b;       /* esc */
1069                 keybuf[1] = '[';
1070                 keybuf[2] = 'C';
1071                 break;
1072         case SCAN_LEFT: /* LEFT */
1073                 keybuf[0] = 0x1b;       /* esc */
1074                 keybuf[1] = '[';
1075                 keybuf[2] = 'D';
1076                 break;
1077         case SCAN_DELETE:
1078                 keybuf[0] = CHAR_BACKSPACE;
1079                 break;
1080         case SCAN_ESC:
1081                 keybuf[0] = 0x1b;       /* esc */
1082                 break;
1083         default:
1084                 keybuf[0] = key->UnicodeChar;
1085                 break;
1086         }
1087 }
1088
1089 static bool
1090 efi_readkey(void)
1091 {
1092         EFI_STATUS status;
1093         EFI_INPUT_KEY key;
1094
1095         status = conin->ReadKeyStroke(conin, &key);
1096         if (status == EFI_SUCCESS) {
1097                 keybuf_inschar(&key);
1098                 return (true);
1099         }
1100         return (false);
1101 }
1102
1103 static bool
1104 efi_readkey_ex(void)
1105 {
1106         EFI_STATUS status;
1107         EFI_INPUT_KEY *kp;
1108         EFI_KEY_DATA  key_data;
1109         uint32_t kss;
1110
1111         status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1112         if (status == EFI_SUCCESS) {
1113                 kss = key_data.KeyState.KeyShiftState;
1114                 kp = &key_data.Key;
1115                 if (kss & EFI_SHIFT_STATE_VALID) {
1116
1117                         /*
1118                          * quick mapping to control chars, replace with
1119                          * map lookup later.
1120                          */
1121                         if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1122                             kss & EFI_LEFT_CONTROL_PRESSED) {
1123                                 if (kp->UnicodeChar >= 'a' &&
1124                                     kp->UnicodeChar <= 'z') {
1125                                         kp->UnicodeChar -= 'a';
1126                                         kp->UnicodeChar++;
1127                                 }
1128                         }
1129                 }
1130                 /*
1131                  * The shift state and/or toggle state may not be valid,
1132                  * but we still can have ScanCode or UnicodeChar.
1133                  */
1134                 if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1135                         return (false);
1136                 keybuf_inschar(kp);
1137                 return (true);
1138         }
1139         return (false);
1140 }
1141
1142 int
1143 efi_cons_getchar(void)
1144 {
1145         int c;
1146
1147         if ((c = keybuf_getchar()) != 0)
1148                 return (c);
1149
1150         key_pending = 0;
1151
1152         if (coninex == NULL) {
1153                 if (efi_readkey())
1154                         return (keybuf_getchar());
1155         } else {
1156                 if (efi_readkey_ex())
1157                         return (keybuf_getchar());
1158         }
1159
1160         return (-1);
1161 }
1162
1163 int
1164 efi_cons_poll(void)
1165 {
1166         EFI_STATUS status;
1167
1168         if (keybuf_ischar() || key_pending)
1169                 return (1);
1170
1171         /*
1172          * Some EFI implementation (u-boot for example) do not support
1173          * WaitForKey().
1174          * CheckEvent() can clear the signaled state.
1175          */
1176         if (coninex != NULL) {
1177                 if (coninex->WaitForKeyEx == NULL) {
1178                         key_pending = efi_readkey_ex();
1179                 } else {
1180                         status = BS->CheckEvent(coninex->WaitForKeyEx);
1181                         key_pending = status == EFI_SUCCESS;
1182                 }
1183         } else {
1184                 if (conin->WaitForKey == NULL) {
1185                         key_pending = efi_readkey();
1186                 } else {
1187                         status = BS->CheckEvent(conin->WaitForKey);
1188                         key_pending = status == EFI_SUCCESS;
1189                 }
1190         }
1191
1192         return (key_pending);
1193 }
1194
1195 /* Plain direct access to EFI OutputString(). */
1196 void
1197 efi_cons_efiputchar(int c)
1198 {
1199         CHAR16 buf[2];
1200         EFI_STATUS status;
1201
1202         buf[0] = c;
1203         buf[1] = 0;     /* terminate string */
1204
1205         status = conout->TestString(conout, buf);
1206         if (EFI_ERROR(status))
1207                 buf[0] = '?';
1208         conout->OutputString(conout, buf);
1209 }