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