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