xref: /freebsd/stand/efi/libefi/efi_console.c (revision 0b37c1590418417c894529d371800dfac71ef887)
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 	 * When doing multiple consoles (both serial and video),
829 	 * also just use the old emulator. RB_MULTIPLE also implies
830 	 * we're using a serial console.
831 	 */
832 	mode = parse_uefi_con_out();
833 	if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
834 		if (buffer != NULL) {
835 			if (tp.tp_row == rows && tp.tp_col == cols)
836 				return (true);
837 			free(buffer);
838 		} else {
839 			teken_init(&teken, &tf, NULL);
840 		}
841 
842 		tp.tp_row = rows;
843 		tp.tp_col = cols;
844 		buffer = malloc(rows * cols * sizeof(*buffer));
845 		if (buffer == NULL)
846 			return (false);
847 
848 		teken_set_winsize(&teken, &tp);
849 		a = teken_get_defattr(&teken);
850 
851 		snprintf(env, sizeof(env), "%d", a->ta_fgcolor);
852 		env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors,
853 		    env_nounset);
854 		snprintf(env, sizeof(env), "%d", a->ta_bgcolor);
855 		env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors,
856 		    env_nounset);
857 
858 		for (int row = 0; row < rows; row++) {
859 			for (int col = 0; col < cols; col++) {
860 				buffer[col + row * tp.tp_col].c = ' ';
861 				buffer[col + row * tp.tp_col].a = *a;
862 			}
863 		}
864 	} else {
865 #ifdef TERM_EMU
866 		conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
867 		    DEFAULT_BGCOLOR));
868 		end_term();
869 		get_pos(&curx, &cury);
870 		curs_move(&curx, &cury, curx, cury);
871 		fg_c = DEFAULT_FGCOLOR;
872 		bg_c = DEFAULT_BGCOLOR;
873 #endif
874 	}
875 
876 	snprintf(env, sizeof (env), "%u", (unsigned)rows);
877 	setenv("LINES", env, 1);
878 	snprintf(env, sizeof (env), "%u", (unsigned)cols);
879 	setenv("COLUMNS", env, 1);
880 
881 	return (true);
882 }
883 
884 static int
885 efi_cons_init(int arg)
886 {
887 	EFI_STATUS status;
888 
889 	if (conin != NULL)
890 		return (0);
891 
892 	conout = ST->ConOut;
893 	conin = ST->ConIn;
894 
895 	conout->EnableCursor(conout, TRUE);
896 	status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
897 	    (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
898 	if (status != EFI_SUCCESS)
899 		coninex = NULL;
900 
901 	if (efi_cons_update_mode())
902 		return (0);
903 
904 	return (1);
905 }
906 
907 static void
908 input_partial(void)
909 {
910 	unsigned i;
911 	uint32_t c;
912 
913 	if (utf8_left == 0)
914 		return;
915 
916 	for (i = 0; i < sizeof(utf8_partial); i++) {
917 		c = (utf8_partial >> (24 - (i << 3))) & 0xff;
918 		if (c != 0)
919 			efi_term_emu(c);
920 	}
921 	utf8_left = 0;
922 	utf8_partial = 0;
923 }
924 
925 static void
926 input_byte(uint8_t c)
927 {
928 	if ((c & 0x80) == 0x00) {
929 		/* One-byte sequence. */
930 		input_partial();
931 		efi_term_emu(c);
932 		return;
933 	}
934 	if ((c & 0xe0) == 0xc0) {
935 		/* Two-byte sequence. */
936 		input_partial();
937 		utf8_left = 1;
938 		utf8_partial = c;
939 		return;
940 	}
941 	if ((c & 0xf0) == 0xe0) {
942 		/* Three-byte sequence. */
943 		input_partial();
944 		utf8_left = 2;
945 		utf8_partial = c;
946 		return;
947 	}
948 	if ((c & 0xf8) == 0xf0) {
949 		/* Four-byte sequence. */
950 		input_partial();
951 		utf8_left = 3;
952 		utf8_partial = c;
953 		return;
954 	}
955 	if ((c & 0xc0) == 0x80) {
956 		/* Invalid state? */
957 		if (utf8_left == 0) {
958 			efi_term_emu(c);
959 			return;
960 		}
961 		utf8_left--;
962 		utf8_partial = (utf8_partial << 8) | c;
963 		if (utf8_left == 0) {
964 			uint32_t v, u;
965 			uint8_t b;
966 
967 			v = 0;
968 			u = utf8_partial;
969 			b = (u >> 24) & 0xff;
970 			if (b != 0) {		/* Four-byte sequence */
971 				v = b & 0x07;
972 				b = (u >> 16) & 0xff;
973 				v = (v << 6) | (b & 0x3f);
974 				b = (u >> 8) & 0xff;
975 				v = (v << 6) | (b & 0x3f);
976 				b = u & 0xff;
977 				v = (v << 6) | (b & 0x3f);
978 			} else if ((b = (u >> 16) & 0xff) != 0) {
979 				v = b & 0x0f;	/* Three-byte sequence */
980 				b = (u >> 8) & 0xff;
981 				v = (v << 6) | (b & 0x3f);
982 				b = u & 0xff;
983 				v = (v << 6) | (b & 0x3f);
984 			} else if ((b = (u >> 8) & 0xff) != 0) {
985 				v = b & 0x1f;	/* Two-byte sequence */
986 				b = u & 0xff;
987 				v = (v << 6) | (b & 0x3f);
988 			}
989 			/* Send unicode char directly to console. */
990 			efi_cons_efiputchar(v);
991 			utf8_partial = 0;
992 		}
993 		return;
994 	}
995 	/* Anything left is illegal in UTF-8 sequence. */
996 	input_partial();
997 	efi_term_emu(c);
998 }
999 
1000 void
1001 efi_cons_putchar(int c)
1002 {
1003 	unsigned char ch = c;
1004 
1005 	/*
1006 	 * Don't use Teken when we're doing pure serial, or a multiple console
1007 	 * with video "primary" because that's also serial.
1008 	 */
1009 	if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0) {
1010 		input_byte(ch);
1011 		return;
1012 	}
1013 
1014 	if (buffer != NULL)
1015 		teken_input(&teken, &ch, sizeof (ch));
1016 	else
1017 		efi_cons_efiputchar(c);
1018 }
1019 
1020 static int
1021 keybuf_getchar(void)
1022 {
1023 	int i, c = 0;
1024 
1025 	for (i = 0; i < KEYBUFSZ; i++) {
1026 		if (keybuf[i] != 0) {
1027 			c = keybuf[i];
1028 			keybuf[i] = 0;
1029 			break;
1030 		}
1031 	}
1032 
1033 	return (c);
1034 }
1035 
1036 static bool
1037 keybuf_ischar(void)
1038 {
1039 	int i;
1040 
1041 	for (i = 0; i < KEYBUFSZ; i++) {
1042 		if (keybuf[i] != 0)
1043 			return (true);
1044 	}
1045 	return (false);
1046 }
1047 
1048 /*
1049  * We are not reading input before keybuf is empty, so we are safe
1050  * just to fill keybuf from the beginning.
1051  */
1052 static void
1053 keybuf_inschar(EFI_INPUT_KEY *key)
1054 {
1055 
1056 	switch (key->ScanCode) {
1057 	case SCAN_UP: /* UP */
1058 		keybuf[0] = 0x1b;	/* esc */
1059 		keybuf[1] = '[';
1060 		keybuf[2] = 'A';
1061 		break;
1062 	case SCAN_DOWN: /* DOWN */
1063 		keybuf[0] = 0x1b;	/* esc */
1064 		keybuf[1] = '[';
1065 		keybuf[2] = 'B';
1066 		break;
1067 	case SCAN_RIGHT: /* RIGHT */
1068 		keybuf[0] = 0x1b;	/* esc */
1069 		keybuf[1] = '[';
1070 		keybuf[2] = 'C';
1071 		break;
1072 	case SCAN_LEFT: /* LEFT */
1073 		keybuf[0] = 0x1b;	/* esc */
1074 		keybuf[1] = '[';
1075 		keybuf[2] = 'D';
1076 		break;
1077 	case SCAN_DELETE:
1078 		keybuf[0] = CHAR_BACKSPACE;
1079 		break;
1080 	case SCAN_ESC:
1081 		keybuf[0] = 0x1b;	/* esc */
1082 		break;
1083 	default:
1084 		keybuf[0] = key->UnicodeChar;
1085 		break;
1086 	}
1087 }
1088 
1089 static bool
1090 efi_readkey(void)
1091 {
1092 	EFI_STATUS status;
1093 	EFI_INPUT_KEY key;
1094 
1095 	status = conin->ReadKeyStroke(conin, &key);
1096 	if (status == EFI_SUCCESS) {
1097 		keybuf_inschar(&key);
1098 		return (true);
1099 	}
1100 	return (false);
1101 }
1102 
1103 static bool
1104 efi_readkey_ex(void)
1105 {
1106 	EFI_STATUS status;
1107 	EFI_INPUT_KEY *kp;
1108 	EFI_KEY_DATA  key_data;
1109 	uint32_t kss;
1110 
1111 	status = coninex->ReadKeyStrokeEx(coninex, &key_data);
1112 	if (status == EFI_SUCCESS) {
1113 		kss = key_data.KeyState.KeyShiftState;
1114 		kp = &key_data.Key;
1115 		if (kss & EFI_SHIFT_STATE_VALID) {
1116 
1117 			/*
1118 			 * quick mapping to control chars, replace with
1119 			 * map lookup later.
1120 			 */
1121 			if (kss & EFI_RIGHT_CONTROL_PRESSED ||
1122 			    kss & EFI_LEFT_CONTROL_PRESSED) {
1123 				if (kp->UnicodeChar >= 'a' &&
1124 				    kp->UnicodeChar <= 'z') {
1125 					kp->UnicodeChar -= 'a';
1126 					kp->UnicodeChar++;
1127 				}
1128 			}
1129 		}
1130 		/*
1131 		 * The shift state and/or toggle state may not be valid,
1132 		 * but we still can have ScanCode or UnicodeChar.
1133 		 */
1134 		if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
1135 			return (false);
1136 		keybuf_inschar(kp);
1137 		return (true);
1138 	}
1139 	return (false);
1140 }
1141 
1142 int
1143 efi_cons_getchar(void)
1144 {
1145 	int c;
1146 
1147 	if ((c = keybuf_getchar()) != 0)
1148 		return (c);
1149 
1150 	key_pending = 0;
1151 
1152 	if (coninex == NULL) {
1153 		if (efi_readkey())
1154 			return (keybuf_getchar());
1155 	} else {
1156 		if (efi_readkey_ex())
1157 			return (keybuf_getchar());
1158 	}
1159 
1160 	return (-1);
1161 }
1162 
1163 int
1164 efi_cons_poll(void)
1165 {
1166 	EFI_STATUS status;
1167 
1168 	if (keybuf_ischar() || key_pending)
1169 		return (1);
1170 
1171 	/*
1172 	 * Some EFI implementation (u-boot for example) do not support
1173 	 * WaitForKey().
1174 	 * CheckEvent() can clear the signaled state.
1175 	 */
1176 	if (coninex != NULL) {
1177 		if (coninex->WaitForKeyEx == NULL) {
1178 			key_pending = efi_readkey_ex();
1179 		} else {
1180 			status = BS->CheckEvent(coninex->WaitForKeyEx);
1181 			key_pending = status == EFI_SUCCESS;
1182 		}
1183 	} else {
1184 		if (conin->WaitForKey == NULL) {
1185 			key_pending = efi_readkey();
1186 		} else {
1187 			status = BS->CheckEvent(conin->WaitForKey);
1188 			key_pending = status == EFI_SUCCESS;
1189 		}
1190 	}
1191 
1192 	return (key_pending);
1193 }
1194 
1195 /* Plain direct access to EFI OutputString(). */
1196 void
1197 efi_cons_efiputchar(int c)
1198 {
1199 	CHAR16 buf[2];
1200 	EFI_STATUS status;
1201 
1202 	buf[0] = c;
1203         buf[1] = 0;     /* terminate string */
1204 
1205 	status = conout->TestString(conout, buf);
1206 	if (EFI_ERROR(status))
1207 		buf[0] = '?';
1208 	conout->OutputString(conout, buf);
1209 }
1210