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