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