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 25
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 gfx_fb_setcolors(&attr, efi_set_colors, env_nounset);
1045 }
1046 }
1047
1048 if (screen_buffer == NULL) {
1049 conout->EnableCursor(conout, TRUE);
1050 #ifdef TERM_EMU
1051 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
1052 DEFAULT_BGCOLOR));
1053 end_term();
1054 get_pos(&curx, &cury);
1055 curs_move(&curx, &cury, curx, cury);
1056 fg_c = DEFAULT_FGCOLOR;
1057 bg_c = DEFAULT_BGCOLOR;
1058 #endif
1059 } else {
1060 /* Improve visibility */
1061 if (attr.ta_bgcolor == TC_WHITE)
1062 attr.ta_bgcolor |= TC_LIGHT;
1063 teken_set_defattr(&gfx_state.tg_teken, &attr);
1064
1065 /* Draw frame around terminal area. */
1066 cons_draw_frame(&attr);
1067 /*
1068 * Erase display, this will also fill our screen
1069 * buffer.
1070 */
1071 teken_input(&gfx_state.tg_teken, "\e[2J", 4);
1072 gfx_state.tg_functions->tf_param(&gfx_state,
1073 TP_SHOWCURSOR, 1);
1074 }
1075
1076 snprintf(env, sizeof (env), "%u", (unsigned)rows);
1077 setenv("LINES", env, 1);
1078 snprintf(env, sizeof (env), "%u", (unsigned)cols);
1079 setenv("COLUMNS", env, 1);
1080
1081 return (true);
1082 }
1083
1084 static int
efi_cons_init(int arg)1085 efi_cons_init(int arg)
1086 {
1087 EFI_STATUS status;
1088
1089 if (efi_started)
1090 return (0);
1091
1092 efi_started = true;
1093
1094 gfx_framework_init();
1095 if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT))
1096 return (0);
1097
1098 return (1);
1099 }
1100
1101 static void
input_partial(void)1102 input_partial(void)
1103 {
1104 unsigned i;
1105 uint32_t c;
1106
1107 if (utf8_left == 0)
1108 return;
1109
1110 for (i = 0; i < sizeof(utf8_partial); i++) {
1111 c = (utf8_partial >> (24 - (i << 3))) & 0xff;
1112 if (c != 0)
1113 efi_term_emu(c);
1114 }
1115 utf8_left = 0;
1116 utf8_partial = 0;
1117 }
1118
1119 static void
input_byte(uint8_t c)1120 input_byte(uint8_t c)
1121 {
1122 if ((c & 0x80) == 0x00) {
1123 /* One-byte sequence. */
1124 input_partial();
1125 efi_term_emu(c);
1126 return;
1127 }
1128 if ((c & 0xe0) == 0xc0) {
1129 /* Two-byte sequence. */
1130 input_partial();
1131 utf8_left = 1;
1132 utf8_partial = c;
1133 return;
1134 }
1135 if ((c & 0xf0) == 0xe0) {
1136 /* Three-byte sequence. */
1137 input_partial();
1138 utf8_left = 2;
1139 utf8_partial = c;
1140 return;
1141 }
1142 if ((c & 0xf8) == 0xf0) {
1143 /* Four-byte sequence. */
1144 input_partial();
1145 utf8_left = 3;
1146 utf8_partial = c;
1147 return;
1148 }
1149 if ((c & 0xc0) == 0x80) {
1150 /* Invalid state? */
1151 if (utf8_left == 0) {
1152 efi_term_emu(c);
1153 return;
1154 }
1155 utf8_left--;
1156 utf8_partial = (utf8_partial << 8) | c;
1157 if (utf8_left == 0) {
1158 uint32_t v, u;
1159 uint8_t b;
1160
1161 v = 0;
1162 u = utf8_partial;
1163 b = (u >> 24) & 0xff;
1164 if (b != 0) { /* Four-byte sequence */
1165 v = b & 0x07;
1166 b = (u >> 16) & 0xff;
1167 v = (v << 6) | (b & 0x3f);
1168 b = (u >> 8) & 0xff;
1169 v = (v << 6) | (b & 0x3f);
1170 b = u & 0xff;
1171 v = (v << 6) | (b & 0x3f);
1172 } else if ((b = (u >> 16) & 0xff) != 0) {
1173 v = b & 0x0f; /* Three-byte sequence */
1174 b = (u >> 8) & 0xff;
1175 v = (v << 6) | (b & 0x3f);
1176 b = u & 0xff;
1177 v = (v << 6) | (b & 0x3f);
1178 } else if ((b = (u >> 8) & 0xff) != 0) {
1179 v = b & 0x1f; /* Two-byte sequence */
1180 b = u & 0xff;
1181 v = (v << 6) | (b & 0x3f);
1182 }
1183 /* Send unicode char directly to console. */
1184 efi_cons_efiputchar(v);
1185 utf8_partial = 0;
1186 }
1187 return;
1188 }
1189 /* Anything left is illegal in UTF-8 sequence. */
1190 input_partial();
1191 efi_term_emu(c);
1192 }
1193
1194 void
efi_cons_putchar(int c)1195 efi_cons_putchar(int c)
1196 {
1197 unsigned char ch = c;
1198
1199 /*
1200 * Don't use Teken when we're doing pure serial, or a multiple console
1201 * with video "primary" because that's also serial.
1202 */
1203 if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) {
1204 input_byte(ch);
1205 return;
1206 }
1207
1208 teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
1209 }
1210
1211 static int
keybuf_getchar(void)1212 keybuf_getchar(void)
1213 {
1214 int i, c = 0;
1215
1216 for (i = 0; i < KEYBUFSZ; i++) {
1217 if (keybuf[i] != 0) {
1218 c = keybuf[i];
1219 keybuf[i] = 0;
1220 break;
1221 }
1222 }
1223
1224 return (c);
1225 }
1226
1227 static bool
keybuf_ischar(void)1228 keybuf_ischar(void)
1229 {
1230 int i;
1231
1232 for (i = 0; i < KEYBUFSZ; i++) {
1233 if (keybuf[i] != 0)
1234 return (true);
1235 }
1236 return (false);
1237 }
1238
1239 /*
1240 * We are not reading input before keybuf is empty, so we are safe
1241 * just to fill keybuf from the beginning.
1242 */
1243 static void
keybuf_inschar(EFI_INPUT_KEY * key)1244 keybuf_inschar(EFI_INPUT_KEY *key)
1245 {
1246
1247 switch (key->ScanCode) {
1248 case SCAN_UP: /* UP */
1249 keybuf[0] = 0x1b; /* esc */
1250 keybuf[1] = '[';
1251 keybuf[2] = 'A';
1252 break;
1253 case SCAN_DOWN: /* DOWN */
1254 keybuf[0] = 0x1b; /* esc */
1255 keybuf[1] = '[';
1256 keybuf[2] = 'B';
1257 break;
1258 case SCAN_RIGHT: /* RIGHT */
1259 keybuf[0] = 0x1b; /* esc */
1260 keybuf[1] = '[';
1261 keybuf[2] = 'C';
1262 break;
1263 case SCAN_LEFT: /* LEFT */
1264 keybuf[0] = 0x1b; /* esc */
1265 keybuf[1] = '[';
1266 keybuf[2] = 'D';
1267 break;
1268 case SCAN_DELETE:
1269 keybuf[0] = CHAR_BACKSPACE;
1270 break;
1271 case SCAN_ESC:
1272 keybuf[0] = 0x1b; /* esc */
1273 break;
1274 default:
1275 keybuf[0] = key->UnicodeChar;
1276 break;
1277 }
1278 }
1279
1280 static bool
efi_readkey(void)1281 efi_readkey(void)
1282 {
1283 EFI_STATUS status;
1284 EFI_INPUT_KEY key;
1285
1286 status = conin->ReadKeyStroke(conin, &key);
1287 if (status == EFI_SUCCESS) {
1288 keybuf_inschar(&key);
1289 return (true);
1290 }
1291 return (false);
1292 }
1293
1294 static bool
efi_readkey_ex(void)1295 efi_readkey_ex(void)
1296 {
1297 EFI_STATUS status;
1298 EFI_INPUT_KEY *kp;
1299 EFI_KEY_DATA key_data;
1300 uint32_t kss;
1301
1302 status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1303 if (status == EFI_SUCCESS) {
1304 kss = key_data.KeyState.KeyShiftState;
1305 kp = &key_data.Key;
1306 if (kss & EFI_SHIFT_STATE_VALID) {
1307
1308 /*
1309 * quick mapping to control chars, replace with
1310 * map lookup later.
1311 */
1312 if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1313 kss & EFI_LEFT_CONTROL_PRESSED) {
1314 if (kp->UnicodeChar >= 'a' &&
1315 kp->UnicodeChar <= 'z') {
1316 kp->UnicodeChar -= 'a';
1317 kp->UnicodeChar++;
1318 }
1319 }
1320 }
1321 /*
1322 * The shift state and/or toggle state may not be valid,
1323 * but we still can have ScanCode or UnicodeChar.
1324 */
1325 if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1326 return (false);
1327 keybuf_inschar(kp);
1328 return (true);
1329 }
1330 return (false);
1331 }
1332
1333 int
efi_cons_getchar(void)1334 efi_cons_getchar(void)
1335 {
1336 int c;
1337
1338 if ((c = keybuf_getchar()) != 0)
1339 return (c);
1340
1341 if (!boot_services_active)
1342 return (-1);
1343
1344 key_pending = 0;
1345
1346 if (coninex == NULL) {
1347 if (efi_readkey())
1348 return (keybuf_getchar());
1349 } else {
1350 if (efi_readkey_ex())
1351 return (keybuf_getchar());
1352 }
1353
1354 return (-1);
1355 }
1356
1357 int
efi_cons_poll(void)1358 efi_cons_poll(void)
1359 {
1360 EFI_STATUS status;
1361
1362 if (keybuf_ischar() || key_pending)
1363 return (1);
1364
1365 if (!boot_services_active)
1366 return (0);
1367
1368 /*
1369 * Some EFI implementation (u-boot for example) do not support
1370 * WaitForKey().
1371 * CheckEvent() can clear the signaled state.
1372 */
1373 if (coninex != NULL) {
1374 if (coninex->WaitForKeyEx == NULL) {
1375 key_pending = efi_readkey_ex();
1376 } else {
1377 status = BS->CheckEvent(coninex->WaitForKeyEx);
1378 key_pending = status == EFI_SUCCESS;
1379 }
1380 } else {
1381 if (conin->WaitForKey == NULL) {
1382 key_pending = efi_readkey();
1383 } else {
1384 status = BS->CheckEvent(conin->WaitForKey);
1385 key_pending = status == EFI_SUCCESS;
1386 }
1387 }
1388
1389 return (key_pending);
1390 }
1391
1392 /* Plain direct access to EFI OutputString(). */
1393 void
efi_cons_efiputchar(int c)1394 efi_cons_efiputchar(int c)
1395 {
1396 CHAR16 buf[2];
1397 EFI_STATUS status;
1398
1399 buf[0] = c;
1400 buf[1] = 0; /* terminate string */
1401
1402 status = conout->TestString(conout, buf);
1403 if (EFI_ERROR(status))
1404 buf[0] = '?';
1405 conout->OutputString(conout, buf);
1406 }
1407