xref: /freebsd/stand/efi/libefi/efi_console.c (revision 564b9ff2a7aa62f1094043b12de56ad75aa30394)
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 /*
381  * Set up conin/conout/coninex to make sure we have input ready.
382  */
383 static void
384 efi_cons_probe(struct console *cp)
385 {
386 	EFI_STATUS status;
387 
388 	conout = ST->ConOut;
389 	conin = ST->ConIn;
390 
391 	status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
392 	    (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
393 	if (status != EFI_SUCCESS)
394 		coninex = NULL;
395 
396 	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
397 }
398 
399 static bool
400 color_name_to_teken(const char *name, int *val)
401 {
402 	if (strcasecmp(name, "black") == 0) {
403 		*val = TC_BLACK;
404 		return (true);
405 	}
406 	if (strcasecmp(name, "red") == 0) {
407 		*val = TC_RED;
408 		return (true);
409 	}
410 	if (strcasecmp(name, "green") == 0) {
411 		*val = TC_GREEN;
412 		return (true);
413 	}
414 	if (strcasecmp(name, "brown") == 0) {
415 		*val = TC_BROWN;
416 		return (true);
417 	}
418 	if (strcasecmp(name, "blue") == 0) {
419 		*val = TC_BLUE;
420 		return (true);
421 	}
422 	if (strcasecmp(name, "magenta") == 0) {
423 		*val = TC_MAGENTA;
424 		return (true);
425 	}
426 	if (strcasecmp(name, "cyan") == 0) {
427 		*val = TC_CYAN;
428 		return (true);
429 	}
430 	if (strcasecmp(name, "white") == 0) {
431 		*val = TC_WHITE;
432 		return (true);
433 	}
434 	return (false);
435 }
436 
437 static int
438 efi_set_colors(struct env_var *ev, int flags, const void *value)
439 {
440 	int val = 0;
441 	char buf[2];
442 	const void *evalue;
443 	const teken_attr_t *ap;
444 	teken_attr_t a;
445 
446 	if (value == NULL)
447 		return (CMD_OK);
448 
449 	if (color_name_to_teken(value, &val)) {
450 		snprintf(buf, sizeof (buf), "%d", val);
451 		evalue = buf;
452 	} else {
453 		char *end;
454 
455 		errno = 0;
456 		val = (int)strtol(value, &end, 0);
457 		if (errno != 0 || *end != '\0') {
458 			printf("Allowed values are either ansi color name or "
459 			    "number from range [0-7].\n");
460 			return (CMD_OK);
461 		}
462 		evalue = value;
463 	}
464 
465 	ap = teken_get_defattr(&teken);
466 	a = *ap;
467 	if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
468 		/* is it already set? */
469 		if (ap->ta_fgcolor == val)
470 			return (CMD_OK);
471 		a.ta_fgcolor = val;
472 	}
473 	if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
474 		/* is it already set? */
475 		if (ap->ta_bgcolor == val)
476 			return (CMD_OK);
477 		a.ta_bgcolor = val;
478 	}
479 	env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
480 	teken_set_defattr(&teken, &a);
481 	return (CMD_OK);
482 }
483 
484 #ifdef TERM_EMU
485 /* Get cursor position. */
486 void
487 get_pos(int *x, int *y)
488 {
489 	*x = conout->Mode->CursorColumn;
490 	*y = conout->Mode->CursorRow;
491 }
492 
493 /* Move cursor to x rows and y cols (0-based). */
494 void
495 curs_move(int *_x, int *_y, int x, int y)
496 {
497 	conout->SetCursorPosition(conout, x, y);
498 	if (_x != NULL)
499 		*_x = conout->Mode->CursorColumn;
500 	if (_y != NULL)
501 		*_y = conout->Mode->CursorRow;
502 }
503 
504 /* Clear internal state of the terminal emulation code. */
505 void
506 end_term(void)
507 {
508 	esc = 0;
509 	argc = -1;
510 }
511 #endif
512 
513 static void
514 efi_cons_rawputchar(int c)
515 {
516 	int i;
517 	UINTN x, y;
518 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
519 
520 	if (c == '\t') {
521 		int n;
522 
523 		n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
524 		for (i = 0; i < n; i++)
525 			efi_cons_rawputchar(' ');
526 	} else {
527 #ifndef TERM_EMU
528 		if (c == '\n')
529 			efi_cons_efiputchar('\r');
530 		efi_cons_efiputchar(c);
531 #else
532 		switch (c) {
533 		case '\r':
534 			curx = 0;
535 			efi_cons_efiputchar('\r');
536 			return;
537 		case '\n':
538 			efi_cons_efiputchar('\n');
539 			efi_cons_efiputchar('\r');
540 			cury++;
541 			if (cury >= y)
542 				cury--;
543 			curx = 0;
544 			return;
545 		case '\b':
546 			if (curx > 0) {
547 				efi_cons_efiputchar('\b');
548 				curx--;
549 			}
550 			return;
551 		default:
552 			efi_cons_efiputchar(c);
553 			curx++;
554 			if (curx > x-1) {
555 				curx = 0;
556 				cury++;
557 			}
558 			if (cury > y-1) {
559 				curx = 0;
560 				cury--;
561 			}
562 		}
563 #endif
564 	}
565 	conout->EnableCursor(conout, TRUE);
566 }
567 
568 #ifdef TERM_EMU
569 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
570 static void
571 bail_out(int c)
572 {
573 	char buf[16], *ch;
574 	int i;
575 
576 	if (esc) {
577 		efi_cons_rawputchar('\033');
578 		if (esc != '\033')
579 			efi_cons_rawputchar(esc);
580 		for (i = 0; i <= argc; ++i) {
581 			sprintf(buf, "%d", args[i]);
582 			ch = buf;
583 			while (*ch)
584 				efi_cons_rawputchar(*ch++);
585 		}
586 	}
587 	efi_cons_rawputchar(c);
588 	end_term();
589 }
590 
591 /* Clear display from current position to end of screen. */
592 static void
593 CD(void)
594 {
595 	int i;
596 	UINTN x, y;
597 
598 	get_pos(&curx, &cury);
599 	if (curx == 0 && cury == 0) {
600 		conout->ClearScreen(conout);
601 		end_term();
602 		return;
603 	}
604 
605 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
606 	CL(0);  /* clear current line from cursor to end */
607 	for (i = cury + 1; i < y-1; i++) {
608 		curs_move(NULL, NULL, 0, i);
609 		CL(0);
610 	}
611 	curs_move(NULL, NULL, curx, cury);
612 	end_term();
613 }
614 
615 /*
616  * Absolute cursor move to args[0] rows and args[1] columns
617  * (the coordinates are 1-based).
618  */
619 static void
620 CM(void)
621 {
622 	if (args[0] > 0)
623 		args[0]--;
624 	if (args[1] > 0)
625 		args[1]--;
626 	curs_move(&curx, &cury, args[1], args[0]);
627 	end_term();
628 }
629 
630 /* Home cursor (left top corner), also called from mode command. */
631 void
632 HO(void)
633 {
634 	argc = 1;
635 	args[0] = args[1] = 1;
636 	CM();
637 }
638 
639 /* Clear line from current position to end of line */
640 static void
641 CL(int direction)
642 {
643 	int i, len;
644 	UINTN x, y;
645 	CHAR16 *line;
646 
647 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
648 	switch (direction) {
649 	case 0:	/* from cursor to end */
650 		len = x - curx + 1;
651 		break;
652 	case 1:	/* from beginning to cursor */
653 		len = curx;
654 		break;
655 	case 2:	/* entire line */
656 		len = x;
657 		break;
658 	default:	/* NOTREACHED */
659 		__unreachable();
660 	}
661 
662 	if (cury == y - 1)
663 		len--;
664 
665 	line = malloc(len * sizeof (CHAR16));
666 	if (line == NULL) {
667 		printf("out of memory\n");
668 		return;
669 	}
670 	for (i = 0; i < len; i++)
671 		line[i] = ' ';
672 	line[len-1] = 0;
673 
674 	if (direction != 0)
675 		curs_move(NULL, NULL, 0, cury);
676 
677 	conout->OutputString(conout, line);
678 	/* restore cursor position */
679 	curs_move(NULL, NULL, curx, cury);
680 	free(line);
681 	end_term();
682 }
683 
684 static void
685 get_arg(int c)
686 {
687 	if (argc < 0)
688 		argc = 0;
689 	args[argc] *= 10;
690 	args[argc] += c - '0';
691 }
692 #endif
693 
694 /* Emulate basic capabilities of cons25 terminal */
695 static void
696 efi_term_emu(int c)
697 {
698 #ifdef TERM_EMU
699 	static int ansi_col[] = {
700 		0, 4, 2, 6, 1, 5, 3, 7
701 	};
702 	int t, i;
703 	EFI_STATUS status;
704 
705 	switch (esc) {
706 	case 0:
707 		switch (c) {
708 		case '\033':
709 			esc = c;
710 			break;
711 		default:
712 			efi_cons_rawputchar(c);
713 			break;
714 		}
715 		break;
716 	case '\033':
717 		switch (c) {
718 		case '[':
719 			esc = c;
720 			args[0] = 0;
721 			argc = -1;
722 			break;
723 		default:
724 			bail_out(c);
725 			break;
726 		}
727 		break;
728 	case '[':
729 		switch (c) {
730 		case ';':
731 			if (argc < 0)
732 				argc = 0;
733 			else if (argc + 1 >= MAXARGS)
734 				bail_out(c);
735 			else
736 				args[++argc] = 0;
737 			break;
738 		case 'H':		/* ho = \E[H */
739 			if (argc < 0)
740 				HO();
741 			else if (argc == 1)
742 				CM();
743 			else
744 				bail_out(c);
745 			break;
746 		case 'J':		/* cd = \E[J */
747 			if (argc < 0)
748 				CD();
749 			else
750 				bail_out(c);
751 			break;
752 		case 'm':
753 			if (argc < 0) {
754 				fg_c = DEFAULT_FGCOLOR;
755 				bg_c = DEFAULT_BGCOLOR;
756 			}
757 			for (i = 0; i <= argc; ++i) {
758 				switch (args[i]) {
759 				case 0:		/* back to normal */
760 					fg_c = DEFAULT_FGCOLOR;
761 					bg_c = DEFAULT_BGCOLOR;
762 					break;
763 				case 1:		/* bold */
764 					fg_c |= 0x8;
765 					break;
766 				case 4:		/* underline */
767 				case 5:		/* blink */
768 					bg_c |= 0x8;
769 					break;
770 				case 7:		/* reverse */
771 					t = fg_c;
772 					fg_c = bg_c;
773 					bg_c = t;
774 					break;
775 				case 22:	/* normal intensity */
776 					fg_c &= ~0x8;
777 					break;
778 				case 24:	/* not underline */
779 				case 25:	/* not blinking */
780 					bg_c &= ~0x8;
781 					break;
782 				case 30: case 31: case 32: case 33:
783 				case 34: case 35: case 36: case 37:
784 					fg_c = ansi_col[args[i] - 30];
785 					break;
786 				case 39:	/* normal */
787 					fg_c = DEFAULT_FGCOLOR;
788 					break;
789 				case 40: case 41: case 42: case 43:
790 				case 44: case 45: case 46: case 47:
791 					bg_c = ansi_col[args[i] - 40];
792 					break;
793 				case 49:	/* normal */
794 					bg_c = DEFAULT_BGCOLOR;
795 					break;
796 				}
797 			}
798 			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
799 			end_term();
800 			break;
801 		default:
802 			if (isdigit(c))
803 				get_arg(c);
804 			else
805 				bail_out(c);
806 			break;
807 		}
808 		break;
809 	default:
810 		bail_out(c);
811 		break;
812 	}
813 #else
814 	efi_cons_rawputchar(c);
815 #endif
816 }
817 
818 bool
819 efi_cons_update_mode(void)
820 {
821 	UINTN cols, rows;
822 	const teken_attr_t *a;
823 	EFI_STATUS status;
824 	char env[8];
825 
826 	status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
827 	if (EFI_ERROR(status)) {
828 		cols = 80;
829 		rows = 24;
830 	}
831 
832 	/*
833 	 * When we have serial port listed in ConOut, use pre-teken emulator,
834 	 * if built with.
835 	 * The problem is, we can not output text on efi and comconsole when
836 	 * efi also has comconsole bound. But then again, we need to have
837 	 * terminal emulator for efi text mode to support the menu.
838 	 * While teken is too expensive to be used on serial console, the
839 	 * pre-teken emulator is light enough to be used on serial console.
840 	 *
841 	 * When doing multiple consoles (both serial and video),
842 	 * also just use the old emulator. RB_MULTIPLE also implies
843 	 * we're using a serial console.
844 	 */
845 	mode = parse_uefi_con_out();
846 	if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
847 		if (buffer != NULL) {
848 			if (tp.tp_row == rows && tp.tp_col == cols)
849 				return (true);
850 			free(buffer);
851 		} else {
852 			teken_init(&teken, &tf, NULL);
853 		}
854 
855 		tp.tp_row = rows;
856 		tp.tp_col = cols;
857 		buffer = malloc(rows * cols * sizeof(*buffer));
858 		if (buffer == NULL)
859 			return (false);
860 
861 		teken_set_winsize(&teken, &tp);
862 		a = teken_get_defattr(&teken);
863 
864 		snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
865 		env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors,
866 		    env_nounset);
867 		snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
868 		env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors,
869 		    env_nounset);
870 
871 		for (int row = 0; row < rows; row++) {
872 			for (int col = 0; col < cols; col++) {
873 				buffer[col + row * tp.tp_col].c = ' ';
874 				buffer[col + row * tp.tp_col].a = *a;
875 			}
876 		}
877 	} else {
878 #ifdef TERM_EMU
879 		conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
880 		    DEFAULT_BGCOLOR));
881 		end_term();
882 		get_pos(&curx, &cury);
883 		curs_move(&curx, &cury, curx, cury);
884 		fg_c = DEFAULT_FGCOLOR;
885 		bg_c = DEFAULT_BGCOLOR;
886 #endif
887 	}
888 
889 	snprintf(env, sizeof (env), "%u", (unsigned)rows);
890 	setenv("LINES", env, 1);
891 	snprintf(env, sizeof (env), "%u", (unsigned)cols);
892 	setenv("COLUMNS", env, 1);
893 
894 	return (true);
895 }
896 
897 static int
898 efi_cons_init(int arg)
899 {
900 	EFI_STATUS status;
901 
902 	if (conin != NULL)
903 		return (0);
904 
905 	conout->EnableCursor(conout, TRUE);
906 	if (efi_cons_update_mode())
907 		return (0);
908 
909 	return (1);
910 }
911 
912 static void
913 input_partial(void)
914 {
915 	unsigned i;
916 	uint32_t c;
917 
918 	if (utf8_left == 0)
919 		return;
920 
921 	for (i = 0; i < sizeof(utf8_partial); i++) {
922 		c = (utf8_partial >> (24 - (i << 3))) & 0xff;
923 		if (c != 0)
924 			efi_term_emu(c);
925 	}
926 	utf8_left = 0;
927 	utf8_partial = 0;
928 }
929 
930 static void
931 input_byte(uint8_t c)
932 {
933 	if ((c & 0x80) == 0x00) {
934 		/* One-byte sequence. */
935 		input_partial();
936 		efi_term_emu(c);
937 		return;
938 	}
939 	if ((c & 0xe0) == 0xc0) {
940 		/* Two-byte sequence. */
941 		input_partial();
942 		utf8_left = 1;
943 		utf8_partial = c;
944 		return;
945 	}
946 	if ((c & 0xf0) == 0xe0) {
947 		/* Three-byte sequence. */
948 		input_partial();
949 		utf8_left = 2;
950 		utf8_partial = c;
951 		return;
952 	}
953 	if ((c & 0xf8) == 0xf0) {
954 		/* Four-byte sequence. */
955 		input_partial();
956 		utf8_left = 3;
957 		utf8_partial = c;
958 		return;
959 	}
960 	if ((c & 0xc0) == 0x80) {
961 		/* Invalid state? */
962 		if (utf8_left == 0) {
963 			efi_term_emu(c);
964 			return;
965 		}
966 		utf8_left--;
967 		utf8_partial = (utf8_partial << 8) | c;
968 		if (utf8_left == 0) {
969 			uint32_t v, u;
970 			uint8_t b;
971 
972 			v = 0;
973 			u = utf8_partial;
974 			b = (u >> 24) & 0xff;
975 			if (b != 0) {		/* Four-byte sequence */
976 				v = b & 0x07;
977 				b = (u >> 16) & 0xff;
978 				v = (v << 6) | (b & 0x3f);
979 				b = (u >> 8) & 0xff;
980 				v = (v << 6) | (b & 0x3f);
981 				b = u & 0xff;
982 				v = (v << 6) | (b & 0x3f);
983 			} else if ((b = (u >> 16) & 0xff) != 0) {
984 				v = b & 0x0f;	/* Three-byte sequence */
985 				b = (u >> 8) & 0xff;
986 				v = (v << 6) | (b & 0x3f);
987 				b = u & 0xff;
988 				v = (v << 6) | (b & 0x3f);
989 			} else if ((b = (u >> 8) & 0xff) != 0) {
990 				v = b & 0x1f;	/* Two-byte sequence */
991 				b = u & 0xff;
992 				v = (v << 6) | (b & 0x3f);
993 			}
994 			/* Send unicode char directly to console. */
995 			efi_cons_efiputchar(v);
996 			utf8_partial = 0;
997 		}
998 		return;
999 	}
1000 	/* Anything left is illegal in UTF-8 sequence. */
1001 	input_partial();
1002 	efi_term_emu(c);
1003 }
1004 
1005 void
1006 efi_cons_putchar(int c)
1007 {
1008 	unsigned char ch = c;
1009 
1010 	/*
1011 	 * Don't use Teken when we're doing pure serial, or a multiple console
1012 	 * with video "primary" because that's also serial.
1013 	 */
1014 	if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0) {
1015 		input_byte(ch);
1016 		return;
1017 	}
1018 
1019 	if (buffer != NULL)
1020 		teken_input(&teken, &ch, sizeof (ch));
1021 	else
1022 		efi_cons_efiputchar(c);
1023 }
1024 
1025 static int
1026 keybuf_getchar(void)
1027 {
1028 	int i, c = 0;
1029 
1030 	for (i = 0; i < KEYBUFSZ; i++) {
1031 		if (keybuf[i] != 0) {
1032 			c = keybuf[i];
1033 			keybuf[i] = 0;
1034 			break;
1035 		}
1036 	}
1037 
1038 	return (c);
1039 }
1040 
1041 static bool
1042 keybuf_ischar(void)
1043 {
1044 	int i;
1045 
1046 	for (i = 0; i < KEYBUFSZ; i++) {
1047 		if (keybuf[i] != 0)
1048 			return (true);
1049 	}
1050 	return (false);
1051 }
1052 
1053 /*
1054  * We are not reading input before keybuf is empty, so we are safe
1055  * just to fill keybuf from the beginning.
1056  */
1057 static void
1058 keybuf_inschar(EFI_INPUT_KEY *key)
1059 {
1060 
1061 	switch (key->ScanCode) {
1062 	case SCAN_UP: /* UP */
1063 		keybuf[0] = 0x1b;	/* esc */
1064 		keybuf[1] = '[';
1065 		keybuf[2] = 'A';
1066 		break;
1067 	case SCAN_DOWN: /* DOWN */
1068 		keybuf[0] = 0x1b;	/* esc */
1069 		keybuf[1] = '[';
1070 		keybuf[2] = 'B';
1071 		break;
1072 	case SCAN_RIGHT: /* RIGHT */
1073 		keybuf[0] = 0x1b;	/* esc */
1074 		keybuf[1] = '[';
1075 		keybuf[2] = 'C';
1076 		break;
1077 	case SCAN_LEFT: /* LEFT */
1078 		keybuf[0] = 0x1b;	/* esc */
1079 		keybuf[1] = '[';
1080 		keybuf[2] = 'D';
1081 		break;
1082 	case SCAN_DELETE:
1083 		keybuf[0] = CHAR_BACKSPACE;
1084 		break;
1085 	case SCAN_ESC:
1086 		keybuf[0] = 0x1b;	/* esc */
1087 		break;
1088 	default:
1089 		keybuf[0] = key->UnicodeChar;
1090 		break;
1091 	}
1092 }
1093 
1094 static bool
1095 efi_readkey(void)
1096 {
1097 	EFI_STATUS status;
1098 	EFI_INPUT_KEY key;
1099 
1100 	status = conin->ReadKeyStroke(conin, &key);
1101 	if (status == EFI_SUCCESS) {
1102 		keybuf_inschar(&key);
1103 		return (true);
1104 	}
1105 	return (false);
1106 }
1107 
1108 static bool
1109 efi_readkey_ex(void)
1110 {
1111 	EFI_STATUS status;
1112 	EFI_INPUT_KEY *kp;
1113 	EFI_KEY_DATA  key_data;
1114 	uint32_t kss;
1115 
1116 	status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1117 	if (status == EFI_SUCCESS) {
1118 		kss = key_data.KeyState.KeyShiftState;
1119 		kp = &key_data.Key;
1120 		if (kss & EFI_SHIFT_STATE_VALID) {
1121 
1122 			/*
1123 			 * quick mapping to control chars, replace with
1124 			 * map lookup later.
1125 			 */
1126 			if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1127 			    kss & EFI_LEFT_CONTROL_PRESSED) {
1128 				if (kp->UnicodeChar >= 'a' &&
1129 				    kp->UnicodeChar <= 'z') {
1130 					kp->UnicodeChar -= 'a';
1131 					kp->UnicodeChar++;
1132 				}
1133 			}
1134 		}
1135 		/*
1136 		 * The shift state and/or toggle state may not be valid,
1137 		 * but we still can have ScanCode or UnicodeChar.
1138 		 */
1139 		if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1140 			return (false);
1141 		keybuf_inschar(kp);
1142 		return (true);
1143 	}
1144 	return (false);
1145 }
1146 
1147 int
1148 efi_cons_getchar(void)
1149 {
1150 	int c;
1151 
1152 	if ((c = keybuf_getchar()) != 0)
1153 		return (c);
1154 
1155 	key_pending = 0;
1156 
1157 	if (coninex == NULL) {
1158 		if (efi_readkey())
1159 			return (keybuf_getchar());
1160 	} else {
1161 		if (efi_readkey_ex())
1162 			return (keybuf_getchar());
1163 	}
1164 
1165 	return (-1);
1166 }
1167 
1168 int
1169 efi_cons_poll(void)
1170 {
1171 	EFI_STATUS status;
1172 
1173 	if (keybuf_ischar() || key_pending)
1174 		return (1);
1175 
1176 	/*
1177 	 * Some EFI implementation (u-boot for example) do not support
1178 	 * WaitForKey().
1179 	 * CheckEvent() can clear the signaled state.
1180 	 */
1181 	if (coninex != NULL) {
1182 		if (coninex->WaitForKeyEx == NULL) {
1183 			key_pending = efi_readkey_ex();
1184 		} else {
1185 			status = BS->CheckEvent(coninex->WaitForKeyEx);
1186 			key_pending = status == EFI_SUCCESS;
1187 		}
1188 	} else {
1189 		if (conin->WaitForKey == NULL) {
1190 			key_pending = efi_readkey();
1191 		} else {
1192 			status = BS->CheckEvent(conin->WaitForKey);
1193 			key_pending = status == EFI_SUCCESS;
1194 		}
1195 	}
1196 
1197 	return (key_pending);
1198 }
1199 
1200 /* Plain direct access to EFI OutputString(). */
1201 void
1202 efi_cons_efiputchar(int c)
1203 {
1204 	CHAR16 buf[2];
1205 	EFI_STATUS status;
1206 
1207 	buf[0] = c;
1208         buf[1] = 0;     /* terminate string */
1209 
1210 	status = conout->TestString(conout, buf);
1211 	if (EFI_ERROR(status))
1212 		buf[0] = '?';
1213 	conout->OutputString(conout, buf);
1214 }
1215