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