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