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