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