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