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