xref: /freebsd/stand/efi/libefi/efi_console.c (revision 48c779cdecb5f803e5fe5d761987e976ca9609db)
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 <efi.h>
31 #include <efilib.h>
32 #include <teken.h>
33 #include <sys/reboot.h>
34 
35 #include "bootstrap.h"
36 
37 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
38 static SIMPLE_TEXT_OUTPUT_INTERFACE	*conout;
39 static SIMPLE_INPUT_INTERFACE		*conin;
40 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
41 
42 static int mode;		/* Does ConOut have serial console? */
43 
44 static uint32_t utf8_left;
45 static uint32_t utf8_partial;
46 #ifdef TERM_EMU
47 #define	DEFAULT_FGCOLOR EFI_LIGHTGRAY
48 #define	DEFAULT_BGCOLOR EFI_BLACK
49 
50 #define	MAXARGS 8
51 static int args[MAXARGS], argc;
52 static int fg_c, bg_c, curx, cury;
53 static int esc;
54 
55 void get_pos(int *x, int *y);
56 void curs_move(int *_x, int *_y, int x, int y);
57 static void CL(int);
58 void HO(void);
59 void end_term(void);
60 #endif
61 
62 static tf_bell_t	efi_cons_bell;
63 static tf_cursor_t	efi_text_cursor;
64 static tf_putchar_t	efi_text_putchar;
65 static tf_fill_t	efi_text_fill;
66 static tf_copy_t	efi_text_copy;
67 static tf_param_t	efi_text_param;
68 static tf_respond_t	efi_cons_respond;
69 
70 static teken_funcs_t tf = {
71 	.tf_bell	= efi_cons_bell,
72 	.tf_cursor	= efi_text_cursor,
73 	.tf_putchar	= efi_text_putchar,
74 	.tf_fill	= efi_text_fill,
75 	.tf_copy	= efi_text_copy,
76 	.tf_param	= efi_text_param,
77 	.tf_respond	= efi_cons_respond,
78 };
79 
80 teken_t teken;
81 teken_pos_t tp;
82 
83 struct text_pixel {
84 	teken_char_t c;
85 	teken_attr_t a;
86 };
87 
88 static struct text_pixel *buffer;
89 
90 #define	KEYBUFSZ 10
91 static unsigned keybuf[KEYBUFSZ];	/* keybuf for extended codes */
92 static int key_pending;
93 
94 static const unsigned char teken_color_to_efi_color[16] = {
95 	EFI_BLACK,
96 	EFI_RED,
97 	EFI_GREEN,
98 	EFI_BROWN,
99 	EFI_BLUE,
100 	EFI_MAGENTA,
101 	EFI_CYAN,
102 	EFI_LIGHTGRAY,
103 	EFI_DARKGRAY,
104 	EFI_LIGHTRED,
105 	EFI_LIGHTGREEN,
106 	EFI_YELLOW,
107 	EFI_LIGHTBLUE,
108 	EFI_LIGHTMAGENTA,
109 	EFI_LIGHTCYAN,
110 	EFI_WHITE
111 };
112 
113 static void efi_cons_probe(struct console *);
114 static int efi_cons_init(int);
115 void efi_cons_putchar(int);
116 int efi_cons_getchar(void);
117 void efi_cons_efiputchar(int);
118 int efi_cons_poll(void);
119 
120 struct console efi_console = {
121 	"efi",
122 	"EFI console",
123 	C_WIDEOUT,
124 	efi_cons_probe,
125 	efi_cons_init,
126 	efi_cons_putchar,
127 	efi_cons_getchar,
128 	efi_cons_poll
129 };
130 
131 /*
132  * Not implemented.
133  */
134 static void
135 efi_cons_bell(void *s __unused)
136 {
137 }
138 
139 static void
140 efi_text_cursor(void *s __unused, const teken_pos_t *p)
141 {
142 	UINTN row, col;
143 
144 	(void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
145 
146 	if (p->tp_col == col)
147 		col = p->tp_col - 1;
148 	else
149 		col = p->tp_col;
150 
151 	if (p->tp_row == row)
152 		row = p->tp_row - 1;
153 	else
154 		row = p->tp_row;
155 
156 	conout->SetCursorPosition(conout, col, row);
157 }
158 
159 static void
160 efi_text_printchar(const teken_pos_t *p, bool autoscroll)
161 {
162 	UINTN a, attr;
163 	struct text_pixel *px;
164 	teken_color_t fg, bg, tmp;
165 
166 	px = buffer + p->tp_col + p->tp_row * tp.tp_col;
167 	a = conout->Mode->Attribute;
168 
169 	fg = teken_256to16(px->a.ta_fgcolor);
170 	bg = teken_256to16(px->a.ta_bgcolor);
171 	if (px->a.ta_format & TF_BOLD)
172 		fg |= TC_LIGHT;
173 	if (px->a.ta_format & TF_BLINK)
174 		bg |= TC_LIGHT;
175 
176 	if (px->a.ta_format & TF_REVERSE) {
177 		tmp = fg;
178 		fg = bg;
179 		bg = tmp;
180 	}
181 
182 	attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg],
183 	    teken_color_to_efi_color[bg] & 0x7);
184 
185 	conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
186 
187 	/* to prvent autoscroll, skip print of lower right char */
188 	if (!autoscroll &&
189 	    p->tp_row == tp.tp_row - 1 &&
190 	    p->tp_col == tp.tp_col - 1)
191 		return;
192 
193 	(void) conout->SetAttribute(conout, attr);
194 	efi_cons_efiputchar(px->c);
195 	(void) conout->SetAttribute(conout, a);
196 }
197 
198 static void
199 efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c,
200     const teken_attr_t *a)
201 {
202 	EFI_STATUS status;
203 	int idx;
204 
205 	idx = p->tp_col + p->tp_row * tp.tp_col;
206 	buffer[idx].c = c;
207 	buffer[idx].a = *a;
208 	efi_text_printchar(p, false);
209 }
210 
211 static void
212 efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
213     const teken_attr_t *a)
214 {
215 	teken_pos_t p;
216 	UINTN row, col;
217 
218 	(void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
219 
220 	conout->EnableCursor(conout, FALSE);
221 	for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
222 	    p.tp_row++)
223 		for (p.tp_col = r->tr_begin.tp_col;
224 		    p.tp_col < r->tr_end.tp_col; p.tp_col++)
225 			efi_text_putchar(s, &p, c, a);
226 	conout->EnableCursor(conout, TRUE);
227 }
228 
229 static bool
230 efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
231 {
232 	if (px1->c != px2->c)
233 		return (false);
234 
235 	if (px1->a.ta_format != px2->a.ta_format)
236 		return (false);
237 	if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
238 		return (false);
239 	if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
240 		return (false);
241 
242 	return (true);
243 }
244 
245 static void
246 efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
247 {
248 	int srow, drow;
249 	int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
250 	teken_pos_t d, s;
251 	bool scroll = false;
252 
253 	/*
254 	 * Copying is a little tricky. We must make sure we do it in
255 	 * correct order, to make sure we don't overwrite our own data.
256 	 */
257 
258 	nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
259 	ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
260 
261 	/*
262 	 * Check if we do copy whole screen.
263 	 */
264 	if (p->tp_row == 0 && p->tp_col == 0 &&
265 	    nrow == tp.tp_row - 2 && ncol == tp.tp_col - 2)
266 		scroll = true;
267 
268 	conout->EnableCursor(conout, FALSE);
269 	if (p->tp_row < r->tr_begin.tp_row) {
270 		/* Copy from bottom to top. */
271 		for (y = 0; y < nrow; y++) {
272 			d.tp_row = p->tp_row + y;
273 			s.tp_row = r->tr_begin.tp_row + y;
274 			drow = d.tp_row * tp.tp_col;
275 			srow = s.tp_row * tp.tp_col;
276 			for (x = 0; x < ncol; x++) {
277 				d.tp_col = p->tp_col + x;
278 				s.tp_col = r->tr_begin.tp_col + x;
279 
280 				if (!efi_same_pixel(
281 				    &buffer[d.tp_col + drow],
282 				    &buffer[s.tp_col + srow])) {
283 					buffer[d.tp_col + drow] =
284 					    buffer[s.tp_col + srow];
285 					if (!scroll)
286 						efi_text_printchar(&d, false);
287 				} else if (scroll) {
288 					/*
289 					 * Draw last char and trigger
290 					 * scroll.
291 					 */
292 					if (y == nrow - 1 &&
293 					    x == ncol - 1) {
294 						efi_text_printchar(&d, true);
295 					}
296 				}
297 			}
298 		}
299 	} else {
300 		/* Copy from top to bottom. */
301 		if (p->tp_col < r->tr_begin.tp_col) {
302 			/* Copy from right to left. */
303 			for (y = nrow - 1; y >= 0; y--) {
304 				d.tp_row = p->tp_row + y;
305 				s.tp_row = r->tr_begin.tp_row + y;
306 				drow = d.tp_row * tp.tp_col;
307 				srow = s.tp_row * tp.tp_col;
308 				for (x = 0; x < ncol; x++) {
309 					d.tp_col = p->tp_col + x;
310 					s.tp_col = r->tr_begin.tp_col + x;
311 
312 					if (!efi_same_pixel(
313 					    &buffer[d.tp_col + drow],
314 					    &buffer[s.tp_col + srow])) {
315 						buffer[d.tp_col + drow] =
316 						    buffer[s.tp_col + srow];
317 						efi_text_printchar(&d, false);
318 					}
319 				}
320 			}
321 		} else {
322 			/* Copy from left to right. */
323 			for (y = nrow - 1; y >= 0; y--) {
324 				d.tp_row = p->tp_row + y;
325 				s.tp_row = r->tr_begin.tp_row + y;
326 				drow = d.tp_row * tp.tp_col;
327 				srow = s.tp_row * tp.tp_col;
328 				for (x = ncol - 1; x >= 0; x--) {
329 					d.tp_col = p->tp_col + x;
330 					s.tp_col = r->tr_begin.tp_col + x;
331 
332 					if (!efi_same_pixel(
333 					    &buffer[d.tp_col + drow],
334 					    &buffer[s.tp_col + srow])) {
335 						buffer[d.tp_col + drow] =
336 						    buffer[s.tp_col + srow];
337 						efi_text_printchar(&d, false);
338 					}
339 				}
340 			}
341 		}
342 	}
343 	conout->EnableCursor(conout, TRUE);
344 }
345 
346 static void
347 efi_text_param(void *s __unused, int cmd, unsigned int value)
348 {
349 	switch (cmd) {
350 	case TP_SETLOCALCURSOR:
351 		/*
352 		 * 0 means normal (usually block), 1 means hidden, and
353 		 * 2 means blinking (always block) for compatibility with
354 		 * syscons.  We don't support any changes except hiding,
355 		 * so must map 2 to 0.
356 		 */
357 		value = (value == 1) ? 0 : 1;
358 		/* FALLTHROUGH */
359 	case TP_SHOWCURSOR:
360 		if (value == 1)
361 			conout->EnableCursor(conout, TRUE);
362 		else
363 			conout->EnableCursor(conout, FALSE);
364 		break;
365 	default:
366 		/* Not yet implemented */
367 		break;
368 	}
369 }
370 
371 /*
372  * Not implemented.
373  */
374 static void
375 efi_cons_respond(void *s __unused, const void *buf __unused,
376     size_t len __unused)
377 {
378 }
379 
380 static void
381 efi_cons_probe(struct console *cp)
382 {
383 	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
384 }
385 
386 static bool
387 color_name_to_teken(const char *name, int *val)
388 {
389 	if (strcasecmp(name, "black") == 0) {
390 		*val = TC_BLACK;
391 		return (true);
392 	}
393 	if (strcasecmp(name, "red") == 0) {
394 		*val = TC_RED;
395 		return (true);
396 	}
397 	if (strcasecmp(name, "green") == 0) {
398 		*val = TC_GREEN;
399 		return (true);
400 	}
401 	if (strcasecmp(name, "brown") == 0) {
402 		*val = TC_BROWN;
403 		return (true);
404 	}
405 	if (strcasecmp(name, "blue") == 0) {
406 		*val = TC_BLUE;
407 		return (true);
408 	}
409 	if (strcasecmp(name, "magenta") == 0) {
410 		*val = TC_MAGENTA;
411 		return (true);
412 	}
413 	if (strcasecmp(name, "cyan") == 0) {
414 		*val = TC_CYAN;
415 		return (true);
416 	}
417 	if (strcasecmp(name, "white") == 0) {
418 		*val = TC_WHITE;
419 		return (true);
420 	}
421 	return (false);
422 }
423 
424 static int
425 efi_set_colors(struct env_var *ev, int flags, const void *value)
426 {
427 	int val = 0;
428 	char buf[2];
429 	const void *evalue;
430 	const teken_attr_t *ap;
431 	teken_attr_t a;
432 
433 	if (value == NULL)
434 		return (CMD_OK);
435 
436 	if (color_name_to_teken(value, &val)) {
437 		snprintf(buf, sizeof (buf), "%d", val);
438 		evalue = buf;
439 	} else {
440 		char *end;
441 
442 		errno = 0;
443 		val = (int)strtol(value, &end, 0);
444 		if (errno != 0 || *end != '\0') {
445 			printf("Allowed values are either ansi color name or "
446 			    "number from range [0-7].\n");
447 			return (CMD_OK);
448 		}
449 		evalue = value;
450 	}
451 
452 	ap = teken_get_defattr(&teken);
453 	a = *ap;
454 	if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
455 		/* is it already set? */
456 		if (ap->ta_fgcolor == val)
457 			return (CMD_OK);
458 		a.ta_fgcolor = val;
459 	}
460 	if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
461 		/* is it already set? */
462 		if (ap->ta_bgcolor == val)
463 			return (CMD_OK);
464 		a.ta_bgcolor = val;
465 	}
466 	env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
467 	teken_set_defattr(&teken, &a);
468 	return (CMD_OK);
469 }
470 
471 #ifdef TERM_EMU
472 /* Get cursor position. */
473 void
474 get_pos(int *x, int *y)
475 {
476 	*x = conout->Mode->CursorColumn;
477 	*y = conout->Mode->CursorRow;
478 }
479 
480 /* Move cursor to x rows and y cols (0-based). */
481 void
482 curs_move(int *_x, int *_y, int x, int y)
483 {
484 	conout->SetCursorPosition(conout, x, y);
485 	if (_x != NULL)
486 		*_x = conout->Mode->CursorColumn;
487 	if (_y != NULL)
488 		*_y = conout->Mode->CursorRow;
489 }
490 
491 /* Clear internal state of the terminal emulation code. */
492 void
493 end_term(void)
494 {
495 	esc = 0;
496 	argc = -1;
497 }
498 #endif
499 
500 static void
501 efi_cons_rawputchar(int c)
502 {
503 	int i;
504 	UINTN x, y;
505 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
506 
507 	if (c == '\t') {
508 		int n;
509 
510 		n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
511 		for (i = 0; i < n; i++)
512 			efi_cons_rawputchar(' ');
513 	} else {
514 #ifndef TERM_EMU
515 		if (c == '\n')
516 			efi_cons_efiputchar('\r');
517 		efi_cons_efiputchar(c);
518 #else
519 		switch (c) {
520 		case '\r':
521 			curx = 0;
522 			efi_cons_efiputchar('\r');
523 			return;
524 		case '\n':
525 			efi_cons_efiputchar('\n');
526 			efi_cons_efiputchar('\r');
527 			cury++;
528 			if (cury >= y)
529 				cury--;
530 			curx = 0;
531 			return;
532 		case '\b':
533 			if (curx > 0) {
534 				efi_cons_efiputchar('\b');
535 				curx--;
536 			}
537 			return;
538 		default:
539 			efi_cons_efiputchar(c);
540 			curx++;
541 			if (curx > x-1) {
542 				curx = 0;
543 				cury++;
544 			}
545 			if (cury > y-1) {
546 				curx = 0;
547 				cury--;
548 			}
549 		}
550 #endif
551 	}
552 	conout->EnableCursor(conout, TRUE);
553 }
554 
555 #ifdef TERM_EMU
556 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
557 static void
558 bail_out(int c)
559 {
560 	char buf[16], *ch;
561 	int i;
562 
563 	if (esc) {
564 		efi_cons_rawputchar('\033');
565 		if (esc != '\033')
566 			efi_cons_rawputchar(esc);
567 		for (i = 0; i <= argc; ++i) {
568 			sprintf(buf, "%d", args[i]);
569 			ch = buf;
570 			while (*ch)
571 				efi_cons_rawputchar(*ch++);
572 		}
573 	}
574 	efi_cons_rawputchar(c);
575 	end_term();
576 }
577 
578 /* Clear display from current position to end of screen. */
579 static void
580 CD(void)
581 {
582 	int i;
583 	UINTN x, y;
584 
585 	get_pos(&curx, &cury);
586 	if (curx == 0 && cury == 0) {
587 		conout->ClearScreen(conout);
588 		end_term();
589 		return;
590 	}
591 
592 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
593 	CL(0);  /* clear current line from cursor to end */
594 	for (i = cury + 1; i < y-1; i++) {
595 		curs_move(NULL, NULL, 0, i);
596 		CL(0);
597 	}
598 	curs_move(NULL, NULL, curx, cury);
599 	end_term();
600 }
601 
602 /*
603  * Absolute cursor move to args[0] rows and args[1] columns
604  * (the coordinates are 1-based).
605  */
606 static void
607 CM(void)
608 {
609 	if (args[0] > 0)
610 		args[0]--;
611 	if (args[1] > 0)
612 		args[1]--;
613 	curs_move(&curx, &cury, args[1], args[0]);
614 	end_term();
615 }
616 
617 /* Home cursor (left top corner), also called from mode command. */
618 void
619 HO(void)
620 {
621 	argc = 1;
622 	args[0] = args[1] = 1;
623 	CM();
624 }
625 
626 /* Clear line from current position to end of line */
627 static void
628 CL(int direction)
629 {
630 	int i, len;
631 	UINTN x, y;
632 	CHAR16 *line;
633 
634 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
635 	switch (direction) {
636 	case 0:	/* from cursor to end */
637 		len = x - curx + 1;
638 		break;
639 	case 1:	/* from beginning to cursor */
640 		len = curx;
641 		break;
642 	case 2:	/* entire line */
643 		len = x;
644 		break;
645 	default:	/* NOTREACHED */
646 		__unreachable();
647 	}
648 
649 	if (cury == y - 1)
650 		len--;
651 
652 	line = malloc(len * sizeof (CHAR16));
653 	if (line == NULL) {
654 		printf("out of memory\n");
655 		return;
656 	}
657 	for (i = 0; i < len; i++)
658 		line[i] = ' ';
659 	line[len-1] = 0;
660 
661 	if (direction != 0)
662 		curs_move(NULL, NULL, 0, cury);
663 
664 	conout->OutputString(conout, line);
665 	/* restore cursor position */
666 	curs_move(NULL, NULL, curx, cury);
667 	free(line);
668 	end_term();
669 }
670 
671 static void
672 get_arg(int c)
673 {
674 	if (argc < 0)
675 		argc = 0;
676 	args[argc] *= 10;
677 	args[argc] += c - '0';
678 }
679 #endif
680 
681 /* Emulate basic capabilities of cons25 terminal */
682 static void
683 efi_term_emu(int c)
684 {
685 #ifdef TERM_EMU
686 	static int ansi_col[] = {
687 		0, 4, 2, 6, 1, 5, 3, 7
688 	};
689 	int t, i;
690 	EFI_STATUS status;
691 
692 	switch (esc) {
693 	case 0:
694 		switch (c) {
695 		case '\033':
696 			esc = c;
697 			break;
698 		default:
699 			efi_cons_rawputchar(c);
700 			break;
701 		}
702 		break;
703 	case '\033':
704 		switch (c) {
705 		case '[':
706 			esc = c;
707 			args[0] = 0;
708 			argc = -1;
709 			break;
710 		default:
711 			bail_out(c);
712 			break;
713 		}
714 		break;
715 	case '[':
716 		switch (c) {
717 		case ';':
718 			if (argc < 0)
719 				argc = 0;
720 			else if (argc + 1 >= MAXARGS)
721 				bail_out(c);
722 			else
723 				args[++argc] = 0;
724 			break;
725 		case 'H':		/* ho = \E[H */
726 			if (argc < 0)
727 				HO();
728 			else if (argc == 1)
729 				CM();
730 			else
731 				bail_out(c);
732 			break;
733 		case 'J':		/* cd = \E[J */
734 			if (argc < 0)
735 				CD();
736 			else
737 				bail_out(c);
738 			break;
739 		case 'm':
740 			if (argc < 0) {
741 				fg_c = DEFAULT_FGCOLOR;
742 				bg_c = DEFAULT_BGCOLOR;
743 			}
744 			for (i = 0; i <= argc; ++i) {
745 				switch (args[i]) {
746 				case 0:		/* back to normal */
747 					fg_c = DEFAULT_FGCOLOR;
748 					bg_c = DEFAULT_BGCOLOR;
749 					break;
750 				case 1:		/* bold */
751 					fg_c |= 0x8;
752 					break;
753 				case 4:		/* underline */
754 				case 5:		/* blink */
755 					bg_c |= 0x8;
756 					break;
757 				case 7:		/* reverse */
758 					t = fg_c;
759 					fg_c = bg_c;
760 					bg_c = t;
761 					break;
762 				case 22:	/* normal intensity */
763 					fg_c &= ~0x8;
764 					break;
765 				case 24:	/* not underline */
766 				case 25:	/* not blinking */
767 					bg_c &= ~0x8;
768 					break;
769 				case 30: case 31: case 32: case 33:
770 				case 34: case 35: case 36: case 37:
771 					fg_c = ansi_col[args[i] - 30];
772 					break;
773 				case 39:	/* normal */
774 					fg_c = DEFAULT_FGCOLOR;
775 					break;
776 				case 40: case 41: case 42: case 43:
777 				case 44: case 45: case 46: case 47:
778 					bg_c = ansi_col[args[i] - 40];
779 					break;
780 				case 49:	/* normal */
781 					bg_c = DEFAULT_BGCOLOR;
782 					break;
783 				}
784 			}
785 			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
786 			end_term();
787 			break;
788 		default:
789 			if (isdigit(c))
790 				get_arg(c);
791 			else
792 				bail_out(c);
793 			break;
794 		}
795 		break;
796 	default:
797 		bail_out(c);
798 		break;
799 	}
800 #else
801 	efi_cons_rawputchar(c);
802 #endif
803 }
804 
805 bool
806 efi_cons_update_mode(void)
807 {
808 	UINTN cols, rows;
809 	const teken_attr_t *a;
810 	EFI_STATUS status;
811 	char env[8];
812 
813 	status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
814 	if (EFI_ERROR(status)) {
815 		cols = 80;
816 		rows = 24;
817 	}
818 
819 	/*
820 	 * When we have serial port listed in ConOut, use pre-teken emulator,
821 	 * if built with.
822 	 * The problem is, we can not output text on efi and comconsole when
823 	 * efi also has comconsole bound. But then again, we need to have
824 	 * terminal emulator for efi text mode to support the menu.
825 	 * While teken is too expensive to be used on serial console, the
826 	 * pre-teken emulator is light enough to be used on serial console.
827 	 */
828 	mode = parse_uefi_con_out();
829 	if ((mode & RB_SERIAL) == 0) {
830 		if (buffer != NULL) {
831 			if (tp.tp_row == rows && tp.tp_col == cols)
832 				return (true);
833 			free(buffer);
834 		} else {
835 			teken_init(&teken, &tf, NULL);
836 		}
837 
838 		tp.tp_row = rows;
839 		tp.tp_col = cols;
840 		buffer = malloc(rows * cols * sizeof(*buffer));
841 		if (buffer == NULL)
842 			return (false);
843 
844 		teken_set_winsize(&teken, &tp);
845 		a = teken_get_defattr(&teken);
846 
847 		snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
848 		env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors,
849 		    env_nounset);
850 		snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
851 		env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors,
852 		    env_nounset);
853 
854 		for (int row = 0; row < rows; row++) {
855 			for (int col = 0; col < cols; col++) {
856 				buffer[col + row * tp.tp_col].c = ' ';
857 				buffer[col + row * tp.tp_col].a = *a;
858 			}
859 		}
860 	} else {
861 #ifdef TERM_EMU
862 		conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
863 		    DEFAULT_BGCOLOR));
864 		end_term();
865 		get_pos(&curx, &cury);
866 		curs_move(&curx, &cury, curx, cury);
867 		fg_c = DEFAULT_FGCOLOR;
868 		bg_c = DEFAULT_BGCOLOR;
869 #endif
870 	}
871 
872 	snprintf(env, sizeof (env), "%u", (unsigned)rows);
873 	setenv("LINES", env, 1);
874 	snprintf(env, sizeof (env), "%u", (unsigned)cols);
875 	setenv("COLUMNS", env, 1);
876 
877 	return (true);
878 }
879 
880 static int
881 efi_cons_init(int arg)
882 {
883 	EFI_STATUS status;
884 
885 	if (conin != NULL)
886 		return (0);
887 
888 	conout = ST->ConOut;
889 	conin = ST->ConIn;
890 
891 	conout->EnableCursor(conout, TRUE);
892 	status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
893 	    (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
894 	if (status != EFI_SUCCESS)
895 		coninex = NULL;
896 
897 	if (efi_cons_update_mode())
898 		return (0);
899 
900 	return (1);
901 }
902 
903 static void
904 input_partial(void)
905 {
906 	unsigned i;
907 	uint32_t c;
908 
909 	if (utf8_left == 0)
910 		return;
911 
912 	for (i = 0; i < sizeof(utf8_partial); i++) {
913 		c = (utf8_partial >> (24 - (i << 3))) & 0xff;
914 		if (c != 0)
915 			efi_term_emu(c);
916 	}
917 	utf8_left = 0;
918 	utf8_partial = 0;
919 }
920 
921 static void
922 input_byte(uint8_t c)
923 {
924 	if ((c & 0x80) == 0x00) {
925 		/* One-byte sequence. */
926 		input_partial();
927 		efi_term_emu(c);
928 		return;
929 	}
930 	if ((c & 0xe0) == 0xc0) {
931 		/* Two-byte sequence. */
932 		input_partial();
933 		utf8_left = 1;
934 		utf8_partial = c;
935 		return;
936 	}
937 	if ((c & 0xf0) == 0xe0) {
938 		/* Three-byte sequence. */
939 		input_partial();
940 		utf8_left = 2;
941 		utf8_partial = c;
942 		return;
943 	}
944 	if ((c & 0xf8) == 0xf0) {
945 		/* Four-byte sequence. */
946 		input_partial();
947 		utf8_left = 3;
948 		utf8_partial = c;
949 		return;
950 	}
951 	if ((c & 0xc0) == 0x80) {
952 		/* Invalid state? */
953 		if (utf8_left == 0) {
954 			efi_term_emu(c);
955 			return;
956 		}
957 		utf8_left--;
958 		utf8_partial = (utf8_partial << 8) | c;
959 		if (utf8_left == 0) {
960 			uint32_t v, u;
961 			uint8_t b;
962 
963 			v = 0;
964 			u = utf8_partial;
965 			b = (u >> 24) & 0xff;
966 			if (b != 0) {		/* Four-byte sequence */
967 				v = b & 0x07;
968 				b = (u >> 16) & 0xff;
969 				v = (v << 6) | (b & 0x3f);
970 				b = (u >> 8) & 0xff;
971 				v = (v << 6) | (b & 0x3f);
972 				b = u & 0xff;
973 				v = (v << 6) | (b & 0x3f);
974 			} else if ((b = (u >> 16) & 0xff) != 0) {
975 				v = b & 0x0f;	/* Three-byte sequence */
976 				b = (u >> 8) & 0xff;
977 				v = (v << 6) | (b & 0x3f);
978 				b = u & 0xff;
979 				v = (v << 6) | (b & 0x3f);
980 			} else if ((b = (u >> 8) & 0xff) != 0) {
981 				v = b & 0x1f;	/* Two-byte sequence */
982 				b = u & 0xff;
983 				v = (v << 6) | (b & 0x3f);
984 			}
985 			/* Send unicode char directly to console. */
986 			efi_cons_efiputchar(v);
987 			utf8_partial = 0;
988 		}
989 		return;
990 	}
991 	/* Anything left is illegal in UTF-8 sequence. */
992 	input_partial();
993 	efi_term_emu(c);
994 }
995 
996 void
997 efi_cons_putchar(int c)
998 {
999 	unsigned char ch = c;
1000 
1001 	if ((mode & RB_SERIAL) != 0) {
1002 		input_byte(ch);
1003 		return;
1004 	}
1005 
1006 	if (buffer != NULL)
1007 		teken_input(&teken, &ch, sizeof (ch));
1008 	else
1009 		efi_cons_efiputchar(c);
1010 }
1011 
1012 static int
1013 keybuf_getchar(void)
1014 {
1015 	int i, c = 0;
1016 
1017 	for (i = 0; i < KEYBUFSZ; i++) {
1018 		if (keybuf[i] != 0) {
1019 			c = keybuf[i];
1020 			keybuf[i] = 0;
1021 			break;
1022 		}
1023 	}
1024 
1025 	return (c);
1026 }
1027 
1028 static bool
1029 keybuf_ischar(void)
1030 {
1031 	int i;
1032 
1033 	for (i = 0; i < KEYBUFSZ; i++) {
1034 		if (keybuf[i] != 0)
1035 			return (true);
1036 	}
1037 	return (false);
1038 }
1039 
1040 /*
1041  * We are not reading input before keybuf is empty, so we are safe
1042  * just to fill keybuf from the beginning.
1043  */
1044 static void
1045 keybuf_inschar(EFI_INPUT_KEY *key)
1046 {
1047 
1048 	switch (key->ScanCode) {
1049 	case SCAN_UP: /* UP */
1050 		keybuf[0] = 0x1b;	/* esc */
1051 		keybuf[1] = '[';
1052 		keybuf[2] = 'A';
1053 		break;
1054 	case SCAN_DOWN: /* DOWN */
1055 		keybuf[0] = 0x1b;	/* esc */
1056 		keybuf[1] = '[';
1057 		keybuf[2] = 'B';
1058 		break;
1059 	case SCAN_RIGHT: /* RIGHT */
1060 		keybuf[0] = 0x1b;	/* esc */
1061 		keybuf[1] = '[';
1062 		keybuf[2] = 'C';
1063 		break;
1064 	case SCAN_LEFT: /* LEFT */
1065 		keybuf[0] = 0x1b;	/* esc */
1066 		keybuf[1] = '[';
1067 		keybuf[2] = 'D';
1068 		break;
1069 	case SCAN_DELETE:
1070 		keybuf[0] = CHAR_BACKSPACE;
1071 		break;
1072 	case SCAN_ESC:
1073 		keybuf[0] = 0x1b;	/* esc */
1074 		break;
1075 	default:
1076 		keybuf[0] = key->UnicodeChar;
1077 		break;
1078 	}
1079 }
1080 
1081 static bool
1082 efi_readkey(void)
1083 {
1084 	EFI_STATUS status;
1085 	EFI_INPUT_KEY key;
1086 
1087 	status = conin->ReadKeyStroke(conin, &key);
1088 	if (status == EFI_SUCCESS) {
1089 		keybuf_inschar(&key);
1090 		return (true);
1091 	}
1092 	return (false);
1093 }
1094 
1095 static bool
1096 efi_readkey_ex(void)
1097 {
1098 	EFI_STATUS status;
1099 	EFI_INPUT_KEY *kp;
1100 	EFI_KEY_DATA  key_data;
1101 	uint32_t kss;
1102 
1103 	status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1104 	if (status == EFI_SUCCESS) {
1105 		kss = key_data.KeyState.KeyShiftState;
1106 		kp = &key_data.Key;
1107 		if (kss & EFI_SHIFT_STATE_VALID) {
1108 
1109 			/*
1110 			 * quick mapping to control chars, replace with
1111 			 * map lookup later.
1112 			 */
1113 			if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1114 			    kss & EFI_LEFT_CONTROL_PRESSED) {
1115 				if (kp->UnicodeChar >= 'a' &&
1116 				    kp->UnicodeChar <= 'z') {
1117 					kp->UnicodeChar -= 'a';
1118 					kp->UnicodeChar++;
1119 				}
1120 			}
1121 		}
1122 
1123 		keybuf_inschar(kp);
1124 		return (true);
1125 	}
1126 	return (false);
1127 }
1128 
1129 int
1130 efi_cons_getchar(void)
1131 {
1132 	int c;
1133 
1134 	if ((c = keybuf_getchar()) != 0)
1135 		return (c);
1136 
1137 	key_pending = 0;
1138 
1139 	if (coninex == NULL) {
1140 		if (efi_readkey())
1141 			return (keybuf_getchar());
1142 	} else {
1143 		if (efi_readkey_ex())
1144 			return (keybuf_getchar());
1145 	}
1146 
1147 	return (-1);
1148 }
1149 
1150 int
1151 efi_cons_poll(void)
1152 {
1153 	EFI_STATUS status;
1154 
1155 	if (keybuf_ischar() || key_pending)
1156 		return (1);
1157 
1158 	/*
1159 	 * Some EFI implementation (u-boot for example) do not support
1160 	 * WaitForKey().
1161 	 * CheckEvent() can clear the signaled state.
1162 	 */
1163 	if (coninex != NULL) {
1164 		if (coninex->WaitForKeyEx == NULL) {
1165 			key_pending = efi_readkey_ex();
1166 		} else {
1167 			status = BS->CheckEvent(coninex->WaitForKeyEx);
1168 			key_pending = status == EFI_SUCCESS;
1169 		}
1170 	} else {
1171 		if (conin->WaitForKey == NULL) {
1172 			key_pending = efi_readkey();
1173 		} else {
1174 			status = BS->CheckEvent(conin->WaitForKey);
1175 			key_pending = status == EFI_SUCCESS;
1176 		}
1177 	}
1178 
1179 	return (key_pending);
1180 }
1181 
1182 /* Plain direct access to EFI OutputString(). */
1183 void
1184 efi_cons_efiputchar(int c)
1185 {
1186 	CHAR16 buf[2];
1187 	EFI_STATUS status;
1188 
1189 	buf[0] = c;
1190         buf[1] = 0;     /* terminate string */
1191 
1192 	status = conout->TestString(conout, buf);
1193 	if (EFI_ERROR(status))
1194 		buf[0] = '?';
1195 	conout->OutputString(conout, buf);
1196 }
1197