xref: /freebsd-src/stand/efi/libefi/efi_console.c (revision 3b68c491d37196bb76a95bce3c02f7c6d5ba22fd)
1ca987d46SWarner Losh /*-
2ca987d46SWarner Losh  * Copyright (c) 2000 Doug Rabson
3ca987d46SWarner Losh  * All rights reserved.
4ca987d46SWarner Losh  *
5ca987d46SWarner Losh  * Redistribution and use in source and binary forms, with or without
6ca987d46SWarner Losh  * modification, are permitted provided that the following conditions
7ca987d46SWarner Losh  * are met:
8ca987d46SWarner Losh  * 1. Redistributions of source code must retain the above copyright
9ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer.
10ca987d46SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
11ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
12ca987d46SWarner Losh  *    documentation and/or other materials provided with the distribution.
13ca987d46SWarner Losh  *
14ca987d46SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15ca987d46SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ca987d46SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ca987d46SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ca987d46SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ca987d46SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ca987d46SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ca987d46SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ca987d46SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ca987d46SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ca987d46SWarner Losh  * SUCH DAMAGE.
25ca987d46SWarner Losh  */
26ca987d46SWarner Losh 
273630506bSToomas Soome #include <sys/param.h>
28ca987d46SWarner Losh #include <efi.h>
29ca987d46SWarner Losh #include <efilib.h>
3056758831SToomas Soome #include <teken.h>
31b9f745fdSToomas Soome #include <sys/reboot.h>
323630506bSToomas Soome #include <machine/metadata.h>
333630506bSToomas Soome #include <gfx_fb.h>
3450180d2bSToomas Soome #include <framebuffer.h>
35ca987d46SWarner Losh #include "bootstrap.h"
36ca987d46SWarner Losh 
373630506bSToomas Soome extern EFI_GUID gop_guid;
38305ef653SWarner Losh 
39305ef653SWarner Losh bool boot_services_active = true; /* boot services active first thing in main */
40305ef653SWarner Losh 
4105b24e86SToomas Soome static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
42ca987d46SWarner Losh static SIMPLE_TEXT_OUTPUT_INTERFACE	*conout;
43ca987d46SWarner Losh static SIMPLE_INPUT_INTERFACE		*conin;
4405b24e86SToomas Soome static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
453630506bSToomas Soome static bool efi_started;
46b9f745fdSToomas Soome static int mode;		/* Does ConOut have serial console? */
47b9f745fdSToomas Soome 
48b9f745fdSToomas Soome static uint32_t utf8_left;
49b9f745fdSToomas Soome static uint32_t utf8_partial;
50b9f745fdSToomas Soome #ifdef TERM_EMU
51b9f745fdSToomas Soome #define	DEFAULT_FGCOLOR EFI_LIGHTGRAY
52b9f745fdSToomas Soome #define	DEFAULT_BGCOLOR EFI_BLACK
53b9f745fdSToomas Soome 
54b9f745fdSToomas Soome #define	MAXARGS 8
55b9f745fdSToomas Soome static int args[MAXARGS], argc;
56b9f745fdSToomas Soome static int fg_c, bg_c, curx, cury;
57b9f745fdSToomas Soome static int esc;
58b9f745fdSToomas Soome 
59b9f745fdSToomas Soome void get_pos(int *x, int *y);
60b9f745fdSToomas Soome void curs_move(int *_x, int *_y, int x, int y);
61b9f745fdSToomas Soome static void CL(int);
62b9f745fdSToomas Soome void HO(void);
63b9f745fdSToomas Soome void end_term(void);
64b9f745fdSToomas Soome #endif
65b9f745fdSToomas Soome 
66*3b68c491SAhmad Khalifa #define	TEXT_ROWS	25
673630506bSToomas Soome #define	TEXT_COLS	80
683630506bSToomas Soome 
6956758831SToomas Soome static tf_bell_t	efi_cons_bell;
7056758831SToomas Soome static tf_cursor_t	efi_text_cursor;
7156758831SToomas Soome static tf_putchar_t	efi_text_putchar;
7256758831SToomas Soome static tf_fill_t	efi_text_fill;
7356758831SToomas Soome static tf_copy_t	efi_text_copy;
7456758831SToomas Soome static tf_param_t	efi_text_param;
7556758831SToomas Soome static tf_respond_t	efi_cons_respond;
76ca987d46SWarner Losh 
7756758831SToomas Soome static teken_funcs_t tf = {
7856758831SToomas Soome 	.tf_bell	= efi_cons_bell,
7956758831SToomas Soome 	.tf_cursor	= efi_text_cursor,
8056758831SToomas Soome 	.tf_putchar	= efi_text_putchar,
8156758831SToomas Soome 	.tf_fill	= efi_text_fill,
8256758831SToomas Soome 	.tf_copy	= efi_text_copy,
8356758831SToomas Soome 	.tf_param	= efi_text_param,
8456758831SToomas Soome 	.tf_respond	= efi_cons_respond,
8556758831SToomas Soome };
86ca987d46SWarner Losh 
873630506bSToomas Soome static teken_funcs_t tfx = {
883630506bSToomas Soome 	.tf_bell	= efi_cons_bell,
893630506bSToomas Soome 	.tf_cursor	= gfx_fb_cursor,
903630506bSToomas Soome 	.tf_putchar	= gfx_fb_putchar,
913630506bSToomas Soome 	.tf_fill	= gfx_fb_fill,
923630506bSToomas Soome 	.tf_copy	= gfx_fb_copy,
933630506bSToomas Soome 	.tf_param	= gfx_fb_param,
943630506bSToomas Soome 	.tf_respond	= efi_cons_respond,
9556758831SToomas Soome };
9656758831SToomas Soome 
97fb0df666SToomas Soome #define	KEYBUFSZ 10
98fb0df666SToomas Soome static unsigned keybuf[KEYBUFSZ];	/* keybuf for extended codes */
99ca987d46SWarner Losh static int key_pending;
100ca987d46SWarner Losh 
10156758831SToomas Soome static const unsigned char teken_color_to_efi_color[16] = {
10256758831SToomas Soome 	EFI_BLACK,
10356758831SToomas Soome 	EFI_RED,
10456758831SToomas Soome 	EFI_GREEN,
10556758831SToomas Soome 	EFI_BROWN,
10656758831SToomas Soome 	EFI_BLUE,
10756758831SToomas Soome 	EFI_MAGENTA,
10856758831SToomas Soome 	EFI_CYAN,
10956758831SToomas Soome 	EFI_LIGHTGRAY,
11056758831SToomas Soome 	EFI_DARKGRAY,
11156758831SToomas Soome 	EFI_LIGHTRED,
11256758831SToomas Soome 	EFI_LIGHTGREEN,
11356758831SToomas Soome 	EFI_YELLOW,
11456758831SToomas Soome 	EFI_LIGHTBLUE,
11556758831SToomas Soome 	EFI_LIGHTMAGENTA,
11656758831SToomas Soome 	EFI_LIGHTCYAN,
11756758831SToomas Soome 	EFI_WHITE
11856758831SToomas Soome };
11956758831SToomas Soome 
120ca987d46SWarner Losh static void efi_cons_probe(struct console *);
121ca987d46SWarner Losh static int efi_cons_init(int);
122ca987d46SWarner Losh void efi_cons_putchar(int);
123ca987d46SWarner Losh int efi_cons_getchar(void);
124ca987d46SWarner Losh void efi_cons_efiputchar(int);
125ca987d46SWarner Losh int efi_cons_poll(void);
1263630506bSToomas Soome static void cons_draw_frame(teken_attr_t *);
127ca987d46SWarner Losh 
128ca987d46SWarner Losh struct console efi_console = {
129b3551da9SWarner Losh 	.c_name = "efi",
130b3551da9SWarner Losh 	.c_desc = "EFI console",
131b3551da9SWarner Losh 	.c_flags = C_WIDEOUT,
132b3551da9SWarner Losh 	.c_probe = efi_cons_probe,
133b3551da9SWarner Losh 	.c_init = efi_cons_init,
134b3551da9SWarner Losh 	.c_out = efi_cons_putchar,
135b3551da9SWarner Losh 	.c_in = efi_cons_getchar,
136b3551da9SWarner Losh 	.c_ready = efi_cons_poll
137ca987d46SWarner Losh };
138ca987d46SWarner Losh 
13956758831SToomas Soome /*
1403630506bSToomas Soome  * This function is used to mark a rectangular image area so the scrolling
1413630506bSToomas Soome  * will know we need to copy the data from there.
1423630506bSToomas Soome  */
1433630506bSToomas Soome void
1443630506bSToomas Soome term_image_display(teken_gfx_t *state, const teken_rect_t *r)
1453630506bSToomas Soome {
1463630506bSToomas Soome 	teken_pos_t p;
1473630506bSToomas Soome 	int idx;
1483630506bSToomas Soome 
14989632acbSToomas Soome 	if (screen_buffer == NULL)
15089632acbSToomas Soome 		return;
15189632acbSToomas Soome 
1523630506bSToomas Soome 	for (p.tp_row = r->tr_begin.tp_row;
1533630506bSToomas Soome 	    p.tp_row < r->tr_end.tp_row; p.tp_row++) {
1543630506bSToomas Soome 		for (p.tp_col = r->tr_begin.tp_col;
1553630506bSToomas Soome 		    p.tp_col < r->tr_end.tp_col; p.tp_col++) {
1563630506bSToomas Soome 			idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
1573630506bSToomas Soome 			if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
1583630506bSToomas Soome 				return;
1593630506bSToomas Soome 			screen_buffer[idx].a.ta_format |= TF_IMAGE;
1603630506bSToomas Soome 		}
1613630506bSToomas Soome 	}
1623630506bSToomas Soome }
1633630506bSToomas Soome 
1643630506bSToomas Soome /*
16556758831SToomas Soome  * Not implemented.
16656758831SToomas Soome  */
16756758831SToomas Soome static void
16856758831SToomas Soome efi_cons_bell(void *s __unused)
169ca987d46SWarner Losh {
170ca987d46SWarner Losh }
171ca987d46SWarner Losh 
17256758831SToomas Soome static void
1733630506bSToomas Soome efi_text_cursor(void *arg, const teken_pos_t *p)
174ca987d46SWarner Losh {
1753630506bSToomas Soome 	teken_gfx_t *state = arg;
1763630506bSToomas Soome 	UINTN col, row;
17756758831SToomas Soome 
178305ef653SWarner Losh 	if (!boot_services_active)
1794c7a3a70SToomas Soome 		return;
1804c7a3a70SToomas Soome 
18156758831SToomas Soome 	row = p->tp_row;
1823630506bSToomas Soome 	if (p->tp_row >= state->tg_tp.tp_row)
1833630506bSToomas Soome 		row = state->tg_tp.tp_row - 1;
1843630506bSToomas Soome 
1853630506bSToomas Soome 	col = p->tp_col;
1863630506bSToomas Soome 	if (p->tp_col >= state->tg_tp.tp_col)
1873630506bSToomas Soome 		col = state->tg_tp.tp_col - 1;
18856758831SToomas Soome 
18956758831SToomas Soome 	conout->SetCursorPosition(conout, col, row);
190ca987d46SWarner Losh }
191ca987d46SWarner Losh 
19256758831SToomas Soome static void
1933630506bSToomas Soome efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll)
194ca987d46SWarner Losh {
19556758831SToomas Soome 	UINTN a, attr;
19656758831SToomas Soome 	struct text_pixel *px;
19756758831SToomas Soome 	teken_color_t fg, bg, tmp;
19856758831SToomas Soome 
1993630506bSToomas Soome 	px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col;
20056758831SToomas Soome 	a = conout->Mode->Attribute;
20156758831SToomas Soome 
20256758831SToomas Soome 	fg = teken_256to16(px->a.ta_fgcolor);
20356758831SToomas Soome 	bg = teken_256to16(px->a.ta_bgcolor);
20456758831SToomas Soome 	if (px->a.ta_format & TF_BOLD)
20556758831SToomas Soome 		fg |= TC_LIGHT;
20656758831SToomas Soome 	if (px->a.ta_format & TF_BLINK)
20756758831SToomas Soome 		bg |= TC_LIGHT;
20856758831SToomas Soome 
20956758831SToomas Soome 	if (px->a.ta_format & TF_REVERSE) {
21056758831SToomas Soome 		tmp = fg;
21156758831SToomas Soome 		fg = bg;
21256758831SToomas Soome 		bg = tmp;
213ca987d46SWarner Losh 	}
214ca987d46SWarner Losh 
21556758831SToomas Soome 	attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg],
21621388f5cSToomas Soome 	    teken_color_to_efi_color[bg] & 0x7);
21756758831SToomas Soome 
21856758831SToomas Soome 	conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
21956758831SToomas Soome 
2203630506bSToomas Soome 	/* to prevent autoscroll, skip print of lower right char */
22103c9cdf7SToomas Soome 	if (!autoscroll &&
2223630506bSToomas Soome 	    p->tp_row == state->tg_tp.tp_row - 1 &&
2233630506bSToomas Soome 	    p->tp_col == state->tg_tp.tp_col - 1)
22456758831SToomas Soome 		return;
22556758831SToomas Soome 
22656758831SToomas Soome 	(void) conout->SetAttribute(conout, attr);
22756758831SToomas Soome 	efi_cons_efiputchar(px->c);
22856758831SToomas Soome 	(void) conout->SetAttribute(conout, a);
22956758831SToomas Soome }
23056758831SToomas Soome 
23156758831SToomas Soome static void
2323630506bSToomas Soome efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c,
23356758831SToomas Soome     const teken_attr_t *a)
23456758831SToomas Soome {
2353630506bSToomas Soome 	teken_gfx_t *state = s;
23656758831SToomas Soome 	EFI_STATUS status;
23756758831SToomas Soome 	int idx;
23856758831SToomas Soome 
239305ef653SWarner Losh 	if (!boot_services_active)
2404c7a3a70SToomas Soome 		return;
2414c7a3a70SToomas Soome 
2423630506bSToomas Soome 	idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
2433630506bSToomas Soome 	if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
2443630506bSToomas Soome 		return;
2453630506bSToomas Soome 
2463630506bSToomas Soome 	screen_buffer[idx].c = c;
2473630506bSToomas Soome 	screen_buffer[idx].a = *a;
2483630506bSToomas Soome 
2493630506bSToomas Soome 	efi_text_printchar(s, p, false);
25056758831SToomas Soome }
25156758831SToomas Soome 
25256758831SToomas Soome static void
2533630506bSToomas Soome efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c,
25456758831SToomas Soome     const teken_attr_t *a)
25556758831SToomas Soome {
2563630506bSToomas Soome 	teken_gfx_t *state = arg;
25756758831SToomas Soome 	teken_pos_t p;
25856758831SToomas Soome 
259305ef653SWarner Losh 	if (!boot_services_active)
2604c7a3a70SToomas Soome 		return;
2614c7a3a70SToomas Soome 
2623630506bSToomas Soome 	if (state->tg_cursor_visible)
26356758831SToomas Soome 		conout->EnableCursor(conout, FALSE);
26456758831SToomas Soome 	for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
26556758831SToomas Soome 	    p.tp_row++)
26656758831SToomas Soome 		for (p.tp_col = r->tr_begin.tp_col;
26756758831SToomas Soome 		    p.tp_col < r->tr_end.tp_col; p.tp_col++)
2683630506bSToomas Soome 			efi_text_putchar(state, &p, c, a);
2693630506bSToomas Soome 	if (state->tg_cursor_visible)
27056758831SToomas Soome 		conout->EnableCursor(conout, TRUE);
27156758831SToomas Soome }
27256758831SToomas Soome 
2733630506bSToomas Soome static void
2743630506bSToomas Soome efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s,
2753630506bSToomas Soome     teken_pos_t *d, bool scroll)
27656758831SToomas Soome {
2773630506bSToomas Soome 	unsigned soffset, doffset;
2783630506bSToomas Soome 	teken_pos_t sp, dp;
2793630506bSToomas Soome 	int x;
28056758831SToomas Soome 
2813630506bSToomas Soome 	soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
2823630506bSToomas Soome 	doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
28356758831SToomas Soome 
2843630506bSToomas Soome 	sp = *s;
2853630506bSToomas Soome 	dp = *d;
2863630506bSToomas Soome 	for (x = 0; x < ncol; x++) {
2873630506bSToomas Soome 		sp.tp_col = s->tp_col + x;
2883630506bSToomas Soome 		dp.tp_col = d->tp_col + x;
2893630506bSToomas Soome 		if (!is_same_pixel(&screen_buffer[soffset + x],
2903630506bSToomas Soome 		    &screen_buffer[doffset + x])) {
2913630506bSToomas Soome 			screen_buffer[doffset + x] =
2923630506bSToomas Soome 			    screen_buffer[soffset + x];
2933630506bSToomas Soome 			if (!scroll)
2943630506bSToomas Soome 				efi_text_printchar(state, &dp, false);
2953630506bSToomas Soome 		} else if (scroll) {
2963630506bSToomas Soome 			/* Draw last char and trigger scroll. */
2973630506bSToomas Soome 			if (dp.tp_col + 1 == state->tg_tp.tp_col &&
2983630506bSToomas Soome 			    dp.tp_row + 1 == state->tg_tp.tp_row) {
2993630506bSToomas Soome 				efi_text_printchar(state, &dp, true);
3003630506bSToomas Soome 			}
3013630506bSToomas Soome 		}
3023630506bSToomas Soome 	}
30356758831SToomas Soome }
30456758831SToomas Soome 
30556758831SToomas Soome static void
3063630506bSToomas Soome efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
30756758831SToomas Soome {
3083630506bSToomas Soome 	teken_gfx_t *state = arg;
3093630506bSToomas Soome 	unsigned doffset, soffset;
31056758831SToomas Soome 	teken_pos_t d, s;
3113630506bSToomas Soome 	int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
31203c9cdf7SToomas Soome 	bool scroll = false;
31356758831SToomas Soome 
314305ef653SWarner Losh 	if (!boot_services_active)
3154c7a3a70SToomas Soome 		return;
3164c7a3a70SToomas Soome 
31756758831SToomas Soome 	/*
31856758831SToomas Soome 	 * Copying is a little tricky. We must make sure we do it in
31956758831SToomas Soome 	 * correct order, to make sure we don't overwrite our own data.
32056758831SToomas Soome 	 */
32156758831SToomas Soome 
32256758831SToomas Soome 	nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
32356758831SToomas Soome 	ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
32456758831SToomas Soome 
32503c9cdf7SToomas Soome 	/*
32603c9cdf7SToomas Soome 	 * Check if we do copy whole screen.
32703c9cdf7SToomas Soome 	 */
32803c9cdf7SToomas Soome 	if (p->tp_row == 0 && p->tp_col == 0 &&
3293630506bSToomas Soome 	    nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2)
33003c9cdf7SToomas Soome 		scroll = true;
33103c9cdf7SToomas Soome 
3323630506bSToomas Soome 	soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
3333630506bSToomas Soome 	doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
3343630506bSToomas Soome 
3353630506bSToomas Soome 	/* remove the cursor */
3363630506bSToomas Soome 	if (state->tg_cursor_visible)
33756758831SToomas Soome 		conout->EnableCursor(conout, FALSE);
33856758831SToomas Soome 
33903c9cdf7SToomas Soome 	/*
3403630506bSToomas Soome 	 * Copy line by line.
34103c9cdf7SToomas Soome 	 */
3423630506bSToomas Soome 	if (doffset <= soffset) {
3433630506bSToomas Soome 		s = r->tr_begin;
3443630506bSToomas Soome 		d = *p;
3453630506bSToomas Soome 		for (y = 0; y < nrow; y++) {
3463630506bSToomas Soome 			s.tp_row = r->tr_begin.tp_row + y;
3473630506bSToomas Soome 			d.tp_row = p->tp_row + y;
3483630506bSToomas Soome 
3493630506bSToomas Soome 			efi_text_copy_line(state, ncol, &s, &d, scroll);
35056758831SToomas Soome 		}
35156758831SToomas Soome 	} else {
35256758831SToomas Soome 		for (y = nrow - 1; y >= 0; y--) {
35356758831SToomas Soome 			s.tp_row = r->tr_begin.tp_row + y;
35456758831SToomas Soome 			d.tp_row = p->tp_row + y;
35556758831SToomas Soome 
3563630506bSToomas Soome 			efi_text_copy_line(state, ncol, &s, &d, false);
35756758831SToomas Soome 		}
35856758831SToomas Soome 	}
3593630506bSToomas Soome 
3603630506bSToomas Soome 	/* display the cursor */
3613630506bSToomas Soome 	if (state->tg_cursor_visible)
36256758831SToomas Soome 		conout->EnableCursor(conout, TRUE);
36356758831SToomas Soome }
36456758831SToomas Soome 
36556758831SToomas Soome static void
3663630506bSToomas Soome efi_text_param(void *arg, int cmd, unsigned int value)
36756758831SToomas Soome {
3683630506bSToomas Soome 	teken_gfx_t *state = arg;
3693630506bSToomas Soome 
370305ef653SWarner Losh 	if (!boot_services_active)
3714c7a3a70SToomas Soome 		return;
3724c7a3a70SToomas Soome 
37356758831SToomas Soome 	switch (cmd) {
37456758831SToomas Soome 	case TP_SETLOCALCURSOR:
37556758831SToomas Soome 		/*
37656758831SToomas Soome 		 * 0 means normal (usually block), 1 means hidden, and
37756758831SToomas Soome 		 * 2 means blinking (always block) for compatibility with
37856758831SToomas Soome 		 * syscons.  We don't support any changes except hiding,
37956758831SToomas Soome 		 * so must map 2 to 0.
38056758831SToomas Soome 		 */
38156758831SToomas Soome 		value = (value == 1) ? 0 : 1;
38256758831SToomas Soome 		/* FALLTHROUGH */
38356758831SToomas Soome 	case TP_SHOWCURSOR:
3843630506bSToomas Soome 		if (value != 0) {
38556758831SToomas Soome 			conout->EnableCursor(conout, TRUE);
3863630506bSToomas Soome 			state->tg_cursor_visible = true;
3873630506bSToomas Soome 		} else {
38856758831SToomas Soome 			conout->EnableCursor(conout, FALSE);
3893630506bSToomas Soome 			state->tg_cursor_visible = false;
3903630506bSToomas Soome 		}
39156758831SToomas Soome 		break;
39256758831SToomas Soome 	default:
39356758831SToomas Soome 		/* Not yet implemented */
39456758831SToomas Soome 		break;
39556758831SToomas Soome 	}
39656758831SToomas Soome }
39756758831SToomas Soome 
39856758831SToomas Soome /*
39956758831SToomas Soome  * Not implemented.
40056758831SToomas Soome  */
40156758831SToomas Soome static void
40256758831SToomas Soome efi_cons_respond(void *s __unused, const void *buf __unused,
40356758831SToomas Soome     size_t len __unused)
40456758831SToomas Soome {
40556758831SToomas Soome }
406ca987d46SWarner Losh 
407ebe8cd79SToomas Soome /*
408ebe8cd79SToomas Soome  * Set up conin/conout/coninex to make sure we have input ready.
409ebe8cd79SToomas Soome  */
410ca987d46SWarner Losh static void
411ca987d46SWarner Losh efi_cons_probe(struct console *cp)
412ca987d46SWarner Losh {
413ebe8cd79SToomas Soome 	EFI_STATUS status;
414ebe8cd79SToomas Soome 
415ebe8cd79SToomas Soome 	conout = ST->ConOut;
416ebe8cd79SToomas Soome 	conin = ST->ConIn;
417ebe8cd79SToomas Soome 
418db316236SToomas Soome 	/*
419db316236SToomas Soome 	 * Call SetMode to work around buggy firmware.
420db316236SToomas Soome 	 */
421db316236SToomas Soome 	status = conout->SetMode(conout, conout->Mode->Mode);
422db316236SToomas Soome 
423db316236SToomas Soome 	if (coninex == NULL) {
424db316236SToomas Soome 		status = BS->OpenProtocol(ST->ConsoleInHandle,
425db316236SToomas Soome 		    &simple_input_ex_guid, (void **)&coninex,
426db316236SToomas Soome 		    IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
427ebe8cd79SToomas Soome 		if (status != EFI_SUCCESS)
428ebe8cd79SToomas Soome 			coninex = NULL;
429db316236SToomas Soome 	}
430ebe8cd79SToomas Soome 
431ca987d46SWarner Losh 	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
432ca987d46SWarner Losh }
433ca987d46SWarner Losh 
434233ab015SToomas Soome static bool
435233ab015SToomas Soome color_name_to_teken(const char *name, int *val)
436233ab015SToomas Soome {
437425e57e7SEd Maste 	int light = 0;
438425e57e7SEd Maste 	if (strncasecmp(name, "light", 5) == 0) {
439425e57e7SEd Maste 		name += 5;
440425e57e7SEd Maste 		light = TC_LIGHT;
441425e57e7SEd Maste 	} else if (strncasecmp(name, "bright", 6) == 0) {
442425e57e7SEd Maste 		name += 6;
443425e57e7SEd Maste 		light = TC_LIGHT;
444425e57e7SEd Maste 	}
445233ab015SToomas Soome 	if (strcasecmp(name, "black") == 0) {
446425e57e7SEd Maste 		*val = TC_BLACK | light;
447233ab015SToomas Soome 		return (true);
448233ab015SToomas Soome 	}
449233ab015SToomas Soome 	if (strcasecmp(name, "red") == 0) {
450425e57e7SEd Maste 		*val = TC_RED | light;
451233ab015SToomas Soome 		return (true);
452233ab015SToomas Soome 	}
453233ab015SToomas Soome 	if (strcasecmp(name, "green") == 0) {
454425e57e7SEd Maste 		*val = TC_GREEN | light;
455233ab015SToomas Soome 		return (true);
456233ab015SToomas Soome 	}
457e9249ef9SEd Maste 	if (strcasecmp(name, "yellow") == 0 || strcasecmp(name, "brown") == 0) {
458cf8880d5SEd Maste 		*val = TC_YELLOW | light;
459233ab015SToomas Soome 		return (true);
460233ab015SToomas Soome 	}
461233ab015SToomas Soome 	if (strcasecmp(name, "blue") == 0) {
462425e57e7SEd Maste 		*val = TC_BLUE | light;
463233ab015SToomas Soome 		return (true);
464233ab015SToomas Soome 	}
465233ab015SToomas Soome 	if (strcasecmp(name, "magenta") == 0) {
466425e57e7SEd Maste 		*val = TC_MAGENTA | light;
467233ab015SToomas Soome 		return (true);
468233ab015SToomas Soome 	}
469233ab015SToomas Soome 	if (strcasecmp(name, "cyan") == 0) {
470425e57e7SEd Maste 		*val = TC_CYAN | light;
471233ab015SToomas Soome 		return (true);
472233ab015SToomas Soome 	}
473233ab015SToomas Soome 	if (strcasecmp(name, "white") == 0) {
474425e57e7SEd Maste 		*val = TC_WHITE | light;
475233ab015SToomas Soome 		return (true);
476233ab015SToomas Soome 	}
477233ab015SToomas Soome 	return (false);
478233ab015SToomas Soome }
479233ab015SToomas Soome 
480233ab015SToomas Soome static int
481233ab015SToomas Soome efi_set_colors(struct env_var *ev, int flags, const void *value)
482233ab015SToomas Soome {
483233ab015SToomas Soome 	int val = 0;
484425e57e7SEd Maste 	char buf[3];
485233ab015SToomas Soome 	const void *evalue;
486233ab015SToomas Soome 	const teken_attr_t *ap;
487233ab015SToomas Soome 	teken_attr_t a;
488233ab015SToomas Soome 
489233ab015SToomas Soome 	if (value == NULL)
490233ab015SToomas Soome 		return (CMD_OK);
491233ab015SToomas Soome 
492233ab015SToomas Soome 	if (color_name_to_teken(value, &val)) {
493233ab015SToomas Soome 		snprintf(buf, sizeof (buf), "%d", val);
494233ab015SToomas Soome 		evalue = buf;
495233ab015SToomas Soome 	} else {
496233ab015SToomas Soome 		char *end;
497425e57e7SEd Maste 		long lval;
498233ab015SToomas Soome 
499233ab015SToomas Soome 		errno = 0;
500425e57e7SEd Maste 		lval = strtol(value, &end, 0);
501425e57e7SEd Maste 		if (errno != 0 || *end != '\0' || lval < 0 || lval > 15) {
502233ab015SToomas Soome 			printf("Allowed values are either ansi color name or "
503425e57e7SEd Maste 			    "number from range [0-15].\n");
504233ab015SToomas Soome 			return (CMD_OK);
505233ab015SToomas Soome 		}
506425e57e7SEd Maste 		val = (int)lval;
507233ab015SToomas Soome 		evalue = value;
508233ab015SToomas Soome 	}
509233ab015SToomas Soome 
5103630506bSToomas Soome 	ap = teken_get_defattr(&gfx_state.tg_teken);
511233ab015SToomas Soome 	a = *ap;
512233ab015SToomas Soome 	if (strcmp(ev->ev_name, "teken.fg_color") == 0) {
513233ab015SToomas Soome 		/* is it already set? */
514233ab015SToomas Soome 		if (ap->ta_fgcolor == val)
515233ab015SToomas Soome 			return (CMD_OK);
516233ab015SToomas Soome 		a.ta_fgcolor = val;
517233ab015SToomas Soome 	}
518233ab015SToomas Soome 	if (strcmp(ev->ev_name, "teken.bg_color") == 0) {
519233ab015SToomas Soome 		/* is it already set? */
520233ab015SToomas Soome 		if (ap->ta_bgcolor == val)
521233ab015SToomas Soome 			return (CMD_OK);
522233ab015SToomas Soome 		a.ta_bgcolor = val;
523233ab015SToomas Soome 	}
5243630506bSToomas Soome 
5253630506bSToomas Soome 	/* Improve visibility */
5263630506bSToomas Soome 	if (a.ta_bgcolor == TC_WHITE)
5273630506bSToomas Soome 		a.ta_bgcolor |= TC_LIGHT;
5283630506bSToomas Soome 
5293630506bSToomas Soome 	teken_set_defattr(&gfx_state.tg_teken, &a);
5303630506bSToomas Soome 	cons_draw_frame(&a);
531233ab015SToomas Soome 	env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
5323630506bSToomas Soome 	teken_input(&gfx_state.tg_teken, "\e[2J", 4);
533233ab015SToomas Soome 	return (CMD_OK);
534233ab015SToomas Soome }
535233ab015SToomas Soome 
536b9f745fdSToomas Soome #ifdef TERM_EMU
537b9f745fdSToomas Soome /* Get cursor position. */
538b9f745fdSToomas Soome void
539b9f745fdSToomas Soome get_pos(int *x, int *y)
540b9f745fdSToomas Soome {
541b9f745fdSToomas Soome 	*x = conout->Mode->CursorColumn;
542b9f745fdSToomas Soome 	*y = conout->Mode->CursorRow;
543b9f745fdSToomas Soome }
544b9f745fdSToomas Soome 
545b9f745fdSToomas Soome /* Move cursor to x rows and y cols (0-based). */
546b9f745fdSToomas Soome void
547b9f745fdSToomas Soome curs_move(int *_x, int *_y, int x, int y)
548b9f745fdSToomas Soome {
549b9f745fdSToomas Soome 	conout->SetCursorPosition(conout, x, y);
550b9f745fdSToomas Soome 	if (_x != NULL)
551b9f745fdSToomas Soome 		*_x = conout->Mode->CursorColumn;
552b9f745fdSToomas Soome 	if (_y != NULL)
553b9f745fdSToomas Soome 		*_y = conout->Mode->CursorRow;
554b9f745fdSToomas Soome }
555b9f745fdSToomas Soome 
556b9f745fdSToomas Soome /* Clear internal state of the terminal emulation code. */
557b9f745fdSToomas Soome void
558b9f745fdSToomas Soome end_term(void)
559b9f745fdSToomas Soome {
560b9f745fdSToomas Soome 	esc = 0;
561b9f745fdSToomas Soome 	argc = -1;
562b9f745fdSToomas Soome }
563b9f745fdSToomas Soome #endif
564b9f745fdSToomas Soome 
565b9f745fdSToomas Soome static void
566b9f745fdSToomas Soome efi_cons_rawputchar(int c)
567b9f745fdSToomas Soome {
568b9f745fdSToomas Soome 	int i;
569b9f745fdSToomas Soome 	UINTN x, y;
570b9f745fdSToomas Soome 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
571b9f745fdSToomas Soome 
572b9f745fdSToomas Soome 	if (c == '\t') {
573b9f745fdSToomas Soome 		int n;
574b9f745fdSToomas Soome 
575b9f745fdSToomas Soome 		n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
576b9f745fdSToomas Soome 		for (i = 0; i < n; i++)
577b9f745fdSToomas Soome 			efi_cons_rawputchar(' ');
578b9f745fdSToomas Soome 	} else {
579b9f745fdSToomas Soome #ifndef TERM_EMU
580b9f745fdSToomas Soome 		if (c == '\n')
581b9f745fdSToomas Soome 			efi_cons_efiputchar('\r');
582b9f745fdSToomas Soome 		efi_cons_efiputchar(c);
583b9f745fdSToomas Soome #else
584b9f745fdSToomas Soome 		switch (c) {
585b9f745fdSToomas Soome 		case '\r':
586b9f745fdSToomas Soome 			curx = 0;
587b9f745fdSToomas Soome 			efi_cons_efiputchar('\r');
588b9f745fdSToomas Soome 			return;
589b9f745fdSToomas Soome 		case '\n':
590b9f745fdSToomas Soome 			efi_cons_efiputchar('\n');
591b9f745fdSToomas Soome 			efi_cons_efiputchar('\r');
592b9f745fdSToomas Soome 			cury++;
593b9f745fdSToomas Soome 			if (cury >= y)
594b9f745fdSToomas Soome 				cury--;
595b9f745fdSToomas Soome 			curx = 0;
596b9f745fdSToomas Soome 			return;
597b9f745fdSToomas Soome 		case '\b':
598b9f745fdSToomas Soome 			if (curx > 0) {
599b9f745fdSToomas Soome 				efi_cons_efiputchar('\b');
600b9f745fdSToomas Soome 				curx--;
601b9f745fdSToomas Soome 			}
602b9f745fdSToomas Soome 			return;
603b9f745fdSToomas Soome 		default:
604b9f745fdSToomas Soome 			efi_cons_efiputchar(c);
605b9f745fdSToomas Soome 			curx++;
606b9f745fdSToomas Soome 			if (curx > x-1) {
607b9f745fdSToomas Soome 				curx = 0;
608b9f745fdSToomas Soome 				cury++;
609b9f745fdSToomas Soome 			}
610b9f745fdSToomas Soome 			if (cury > y-1) {
611b9f745fdSToomas Soome 				curx = 0;
612b9f745fdSToomas Soome 				cury--;
613b9f745fdSToomas Soome 			}
614b9f745fdSToomas Soome 		}
615b9f745fdSToomas Soome #endif
616b9f745fdSToomas Soome 	}
617b9f745fdSToomas Soome 	conout->EnableCursor(conout, TRUE);
618b9f745fdSToomas Soome }
619b9f745fdSToomas Soome 
620b9f745fdSToomas Soome #ifdef TERM_EMU
621b9f745fdSToomas Soome /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
622b9f745fdSToomas Soome static void
623b9f745fdSToomas Soome bail_out(int c)
624b9f745fdSToomas Soome {
625b9f745fdSToomas Soome 	char buf[16], *ch;
626b9f745fdSToomas Soome 	int i;
627b9f745fdSToomas Soome 
628b9f745fdSToomas Soome 	if (esc) {
629b9f745fdSToomas Soome 		efi_cons_rawputchar('\033');
630b9f745fdSToomas Soome 		if (esc != '\033')
631b9f745fdSToomas Soome 			efi_cons_rawputchar(esc);
632b9f745fdSToomas Soome 		for (i = 0; i <= argc; ++i) {
633b9f745fdSToomas Soome 			sprintf(buf, "%d", args[i]);
634b9f745fdSToomas Soome 			ch = buf;
635b9f745fdSToomas Soome 			while (*ch)
636b9f745fdSToomas Soome 				efi_cons_rawputchar(*ch++);
637b9f745fdSToomas Soome 		}
638b9f745fdSToomas Soome 	}
639b9f745fdSToomas Soome 	efi_cons_rawputchar(c);
640b9f745fdSToomas Soome 	end_term();
641b9f745fdSToomas Soome }
642b9f745fdSToomas Soome 
643b9f745fdSToomas Soome /* Clear display from current position to end of screen. */
644b9f745fdSToomas Soome static void
645b9f745fdSToomas Soome CD(void)
646b9f745fdSToomas Soome {
647b9f745fdSToomas Soome 	int i;
648b9f745fdSToomas Soome 	UINTN x, y;
649b9f745fdSToomas Soome 
650b9f745fdSToomas Soome 	get_pos(&curx, &cury);
651b9f745fdSToomas Soome 	if (curx == 0 && cury == 0) {
652b9f745fdSToomas Soome 		conout->ClearScreen(conout);
653b9f745fdSToomas Soome 		end_term();
654b9f745fdSToomas Soome 		return;
655b9f745fdSToomas Soome 	}
656b9f745fdSToomas Soome 
657b9f745fdSToomas Soome 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
658b9f745fdSToomas Soome 	CL(0);  /* clear current line from cursor to end */
659b9f745fdSToomas Soome 	for (i = cury + 1; i < y-1; i++) {
660b9f745fdSToomas Soome 		curs_move(NULL, NULL, 0, i);
661b9f745fdSToomas Soome 		CL(0);
662b9f745fdSToomas Soome 	}
663b9f745fdSToomas Soome 	curs_move(NULL, NULL, curx, cury);
664b9f745fdSToomas Soome 	end_term();
665b9f745fdSToomas Soome }
666b9f745fdSToomas Soome 
667b9f745fdSToomas Soome /*
668b9f745fdSToomas Soome  * Absolute cursor move to args[0] rows and args[1] columns
669b9f745fdSToomas Soome  * (the coordinates are 1-based).
670b9f745fdSToomas Soome  */
671b9f745fdSToomas Soome static void
672b9f745fdSToomas Soome CM(void)
673b9f745fdSToomas Soome {
674b9f745fdSToomas Soome 	if (args[0] > 0)
675b9f745fdSToomas Soome 		args[0]--;
676b9f745fdSToomas Soome 	if (args[1] > 0)
677b9f745fdSToomas Soome 		args[1]--;
678b9f745fdSToomas Soome 	curs_move(&curx, &cury, args[1], args[0]);
679b9f745fdSToomas Soome 	end_term();
680b9f745fdSToomas Soome }
681b9f745fdSToomas Soome 
682b9f745fdSToomas Soome /* Home cursor (left top corner), also called from mode command. */
683b9f745fdSToomas Soome void
684b9f745fdSToomas Soome HO(void)
685b9f745fdSToomas Soome {
686b9f745fdSToomas Soome 	argc = 1;
687b9f745fdSToomas Soome 	args[0] = args[1] = 1;
688b9f745fdSToomas Soome 	CM();
689b9f745fdSToomas Soome }
690b9f745fdSToomas Soome 
691b9f745fdSToomas Soome /* Clear line from current position to end of line */
692b9f745fdSToomas Soome static void
693b9f745fdSToomas Soome CL(int direction)
694b9f745fdSToomas Soome {
695b9f745fdSToomas Soome 	int i, len;
696b9f745fdSToomas Soome 	UINTN x, y;
697b9f745fdSToomas Soome 	CHAR16 *line;
698b9f745fdSToomas Soome 
699b9f745fdSToomas Soome 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
700b9f745fdSToomas Soome 	switch (direction) {
701b9f745fdSToomas Soome 	case 0:	/* from cursor to end */
702b9f745fdSToomas Soome 		len = x - curx + 1;
703b9f745fdSToomas Soome 		break;
704b9f745fdSToomas Soome 	case 1:	/* from beginning to cursor */
705b9f745fdSToomas Soome 		len = curx;
706b9f745fdSToomas Soome 		break;
707b9f745fdSToomas Soome 	case 2:	/* entire line */
708b9f745fdSToomas Soome 		len = x;
709b9f745fdSToomas Soome 		break;
710b9f745fdSToomas Soome 	default:	/* NOTREACHED */
711b9f745fdSToomas Soome 		__unreachable();
712b9f745fdSToomas Soome 	}
713b9f745fdSToomas Soome 
714b9f745fdSToomas Soome 	if (cury == y - 1)
715b9f745fdSToomas Soome 		len--;
716b9f745fdSToomas Soome 
717b9f745fdSToomas Soome 	line = malloc(len * sizeof (CHAR16));
718b9f745fdSToomas Soome 	if (line == NULL) {
719b9f745fdSToomas Soome 		printf("out of memory\n");
720b9f745fdSToomas Soome 		return;
721b9f745fdSToomas Soome 	}
722b9f745fdSToomas Soome 	for (i = 0; i < len; i++)
723b9f745fdSToomas Soome 		line[i] = ' ';
724b9f745fdSToomas Soome 	line[len-1] = 0;
725b9f745fdSToomas Soome 
726b9f745fdSToomas Soome 	if (direction != 0)
727b9f745fdSToomas Soome 		curs_move(NULL, NULL, 0, cury);
728b9f745fdSToomas Soome 
729b9f745fdSToomas Soome 	conout->OutputString(conout, line);
730b9f745fdSToomas Soome 	/* restore cursor position */
731b9f745fdSToomas Soome 	curs_move(NULL, NULL, curx, cury);
732b9f745fdSToomas Soome 	free(line);
733b9f745fdSToomas Soome 	end_term();
734b9f745fdSToomas Soome }
735b9f745fdSToomas Soome 
736b9f745fdSToomas Soome static void
737b9f745fdSToomas Soome get_arg(int c)
738b9f745fdSToomas Soome {
739b9f745fdSToomas Soome 	if (argc < 0)
740b9f745fdSToomas Soome 		argc = 0;
741b9f745fdSToomas Soome 	args[argc] *= 10;
742b9f745fdSToomas Soome 	args[argc] += c - '0';
743b9f745fdSToomas Soome }
744b9f745fdSToomas Soome #endif
745b9f745fdSToomas Soome 
746b9f745fdSToomas Soome /* Emulate basic capabilities of cons25 terminal */
747b9f745fdSToomas Soome static void
748b9f745fdSToomas Soome efi_term_emu(int c)
749b9f745fdSToomas Soome {
750305ef653SWarner Losh 	if (!boot_services_active)
751305ef653SWarner Losh 		return;
752b9f745fdSToomas Soome #ifdef TERM_EMU
753b9f745fdSToomas Soome 	static int ansi_col[] = {
754b9f745fdSToomas Soome 		0, 4, 2, 6, 1, 5, 3, 7
755b9f745fdSToomas Soome 	};
756b9f745fdSToomas Soome 	int t, i;
757b9f745fdSToomas Soome 	EFI_STATUS status;
758b9f745fdSToomas Soome 
759b9f745fdSToomas Soome 	switch (esc) {
760b9f745fdSToomas Soome 	case 0:
761b9f745fdSToomas Soome 		switch (c) {
762b9f745fdSToomas Soome 		case '\033':
763b9f745fdSToomas Soome 			esc = c;
764b9f745fdSToomas Soome 			break;
765b9f745fdSToomas Soome 		default:
766b9f745fdSToomas Soome 			efi_cons_rawputchar(c);
767b9f745fdSToomas Soome 			break;
768b9f745fdSToomas Soome 		}
769b9f745fdSToomas Soome 		break;
770b9f745fdSToomas Soome 	case '\033':
771b9f745fdSToomas Soome 		switch (c) {
772b9f745fdSToomas Soome 		case '[':
773b9f745fdSToomas Soome 			esc = c;
774b9f745fdSToomas Soome 			args[0] = 0;
775b9f745fdSToomas Soome 			argc = -1;
776b9f745fdSToomas Soome 			break;
777b9f745fdSToomas Soome 		default:
778b9f745fdSToomas Soome 			bail_out(c);
779b9f745fdSToomas Soome 			break;
780b9f745fdSToomas Soome 		}
781b9f745fdSToomas Soome 		break;
782b9f745fdSToomas Soome 	case '[':
783b9f745fdSToomas Soome 		switch (c) {
784b9f745fdSToomas Soome 		case ';':
785b9f745fdSToomas Soome 			if (argc < 0)
786b9f745fdSToomas Soome 				argc = 0;
787b9f745fdSToomas Soome 			else if (argc + 1 >= MAXARGS)
788b9f745fdSToomas Soome 				bail_out(c);
789b9f745fdSToomas Soome 			else
790b9f745fdSToomas Soome 				args[++argc] = 0;
791b9f745fdSToomas Soome 			break;
792b9f745fdSToomas Soome 		case 'H':		/* ho = \E[H */
793b9f745fdSToomas Soome 			if (argc < 0)
794b9f745fdSToomas Soome 				HO();
795b9f745fdSToomas Soome 			else if (argc == 1)
796b9f745fdSToomas Soome 				CM();
797b9f745fdSToomas Soome 			else
798b9f745fdSToomas Soome 				bail_out(c);
799b9f745fdSToomas Soome 			break;
800b9f745fdSToomas Soome 		case 'J':		/* cd = \E[J */
801b9f745fdSToomas Soome 			if (argc < 0)
802b9f745fdSToomas Soome 				CD();
803b9f745fdSToomas Soome 			else
804b9f745fdSToomas Soome 				bail_out(c);
805b9f745fdSToomas Soome 			break;
806b9f745fdSToomas Soome 		case 'm':
807b9f745fdSToomas Soome 			if (argc < 0) {
808b9f745fdSToomas Soome 				fg_c = DEFAULT_FGCOLOR;
809b9f745fdSToomas Soome 				bg_c = DEFAULT_BGCOLOR;
810b9f745fdSToomas Soome 			}
811b9f745fdSToomas Soome 			for (i = 0; i <= argc; ++i) {
812b9f745fdSToomas Soome 				switch (args[i]) {
813b9f745fdSToomas Soome 				case 0:		/* back to normal */
814b9f745fdSToomas Soome 					fg_c = DEFAULT_FGCOLOR;
815b9f745fdSToomas Soome 					bg_c = DEFAULT_BGCOLOR;
816b9f745fdSToomas Soome 					break;
817b9f745fdSToomas Soome 				case 1:		/* bold */
818b9f745fdSToomas Soome 					fg_c |= 0x8;
819b9f745fdSToomas Soome 					break;
820b9f745fdSToomas Soome 				case 4:		/* underline */
821b9f745fdSToomas Soome 				case 5:		/* blink */
822b9f745fdSToomas Soome 					bg_c |= 0x8;
823b9f745fdSToomas Soome 					break;
824b9f745fdSToomas Soome 				case 7:		/* reverse */
825b9f745fdSToomas Soome 					t = fg_c;
826b9f745fdSToomas Soome 					fg_c = bg_c;
827b9f745fdSToomas Soome 					bg_c = t;
828b9f745fdSToomas Soome 					break;
829b9f745fdSToomas Soome 				case 22:	/* normal intensity */
830b9f745fdSToomas Soome 					fg_c &= ~0x8;
831b9f745fdSToomas Soome 					break;
832b9f745fdSToomas Soome 				case 24:	/* not underline */
833b9f745fdSToomas Soome 				case 25:	/* not blinking */
834b9f745fdSToomas Soome 					bg_c &= ~0x8;
835b9f745fdSToomas Soome 					break;
836b9f745fdSToomas Soome 				case 30: case 31: case 32: case 33:
837b9f745fdSToomas Soome 				case 34: case 35: case 36: case 37:
838b9f745fdSToomas Soome 					fg_c = ansi_col[args[i] - 30];
839b9f745fdSToomas Soome 					break;
840b9f745fdSToomas Soome 				case 39:	/* normal */
841b9f745fdSToomas Soome 					fg_c = DEFAULT_FGCOLOR;
842b9f745fdSToomas Soome 					break;
843b9f745fdSToomas Soome 				case 40: case 41: case 42: case 43:
844b9f745fdSToomas Soome 				case 44: case 45: case 46: case 47:
845b9f745fdSToomas Soome 					bg_c = ansi_col[args[i] - 40];
846b9f745fdSToomas Soome 					break;
847b9f745fdSToomas Soome 				case 49:	/* normal */
848b9f745fdSToomas Soome 					bg_c = DEFAULT_BGCOLOR;
849b9f745fdSToomas Soome 					break;
850b9f745fdSToomas Soome 				}
851b9f745fdSToomas Soome 			}
852b9f745fdSToomas Soome 			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
853b9f745fdSToomas Soome 			end_term();
854b9f745fdSToomas Soome 			break;
855b9f745fdSToomas Soome 		default:
856b9f745fdSToomas Soome 			if (isdigit(c))
857b9f745fdSToomas Soome 				get_arg(c);
858b9f745fdSToomas Soome 			else
859b9f745fdSToomas Soome 				bail_out(c);
860b9f745fdSToomas Soome 			break;
861b9f745fdSToomas Soome 		}
862b9f745fdSToomas Soome 		break;
863b9f745fdSToomas Soome 	default:
864b9f745fdSToomas Soome 		bail_out(c);
865b9f745fdSToomas Soome 		break;
866b9f745fdSToomas Soome 	}
867b9f745fdSToomas Soome #else
868b9f745fdSToomas Soome 	efi_cons_rawputchar(c);
869b9f745fdSToomas Soome #endif
870b9f745fdSToomas Soome }
871b9f745fdSToomas Soome 
8723630506bSToomas Soome static int
8733630506bSToomas Soome env_screen_nounset(struct env_var *ev __unused)
8743630506bSToomas Soome {
8753630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
8763630506bSToomas Soome 		return (0);
8773630506bSToomas Soome 	return (EPERM);
8783630506bSToomas Soome }
8793630506bSToomas Soome 
8803630506bSToomas Soome static void
8813630506bSToomas Soome cons_draw_frame(teken_attr_t *a)
8823630506bSToomas Soome {
8833630506bSToomas Soome 	teken_attr_t attr = *a;
8843630506bSToomas Soome 	teken_color_t fg = a->ta_fgcolor;
8853630506bSToomas Soome 
8863630506bSToomas Soome 	attr.ta_fgcolor = attr.ta_bgcolor;
8873630506bSToomas Soome 	teken_set_defattr(&gfx_state.tg_teken, &attr);
8883630506bSToomas Soome 
8893630506bSToomas Soome 	gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width,
8903630506bSToomas Soome 	    gfx_state.tg_origin.tp_row, 1);
8913630506bSToomas Soome 	gfx_fb_drawrect(0,
8923630506bSToomas Soome 	    gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1,
8933630506bSToomas Soome 	    gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1);
8943630506bSToomas Soome 	gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row,
8953630506bSToomas Soome 	    gfx_state.tg_origin.tp_col,
8963630506bSToomas Soome 	    gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1);
8973630506bSToomas Soome 	gfx_fb_drawrect(
8983630506bSToomas Soome 	    gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1,
8993630506bSToomas Soome 	    gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width,
9003630506bSToomas Soome 	    gfx_state.tg_fb.fb_height, 1);
9013630506bSToomas Soome 
9023630506bSToomas Soome 	attr.ta_fgcolor = fg;
9033630506bSToomas Soome 	teken_set_defattr(&gfx_state.tg_teken, &attr);
9043630506bSToomas Soome }
9053630506bSToomas Soome 
90656758831SToomas Soome bool
9073630506bSToomas Soome cons_update_mode(bool use_gfx_mode)
90856758831SToomas Soome {
90956758831SToomas Soome 	UINTN cols, rows;
91056758831SToomas Soome 	const teken_attr_t *a;
911a536ed41SToomas Soome 	teken_attr_t attr;
91256758831SToomas Soome 	EFI_STATUS status;
9133630506bSToomas Soome 	char env[10], *ptr;
9143630506bSToomas Soome 
91561c50cbcSToomas Soome 	if (!efi_started)
91661c50cbcSToomas Soome 		return (false);
91761c50cbcSToomas Soome 
9183630506bSToomas Soome 	/*
91950180d2bSToomas Soome 	 * Despite the use_gfx_mode, we want to make sure we call
92050180d2bSToomas Soome 	 * efi_find_framebuffer(). This will populate the fb data,
92150180d2bSToomas Soome 	 * which will be passed to kernel.
9223630506bSToomas Soome 	 */
92350180d2bSToomas Soome 	if (efi_find_framebuffer(&gfx_state) == 0 && use_gfx_mode) {
9243630506bSToomas Soome 		int roff, goff, boff;
9253630506bSToomas Soome 
92650180d2bSToomas Soome 		roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
92750180d2bSToomas Soome 		goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
92850180d2bSToomas Soome 		boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
9293630506bSToomas Soome 
9303630506bSToomas Soome 		(void) generate_cons_palette(cmap, COLOR_FORMAT_RGB,
93150180d2bSToomas Soome 		    gfx_state.tg_fb.fb_mask_red >> roff, roff,
93250180d2bSToomas Soome 		    gfx_state.tg_fb.fb_mask_green >> goff, goff,
93350180d2bSToomas Soome 		    gfx_state.tg_fb.fb_mask_blue >> boff, boff);
9343630506bSToomas Soome 	} else {
93550180d2bSToomas Soome 		/*
93650180d2bSToomas Soome 		 * Either text mode was asked by user or we failed to
93750180d2bSToomas Soome 		 * find frame buffer.
93850180d2bSToomas Soome 		 */
9393630506bSToomas Soome 		gfx_state.tg_fb_type = FB_TEXT;
9403630506bSToomas Soome 	}
94156758831SToomas Soome 
94256758831SToomas Soome 	status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
943db316236SToomas Soome 	if (EFI_ERROR(status) || cols * rows == 0) {
9443630506bSToomas Soome 		cols = TEXT_COLS;
9453630506bSToomas Soome 		rows = TEXT_ROWS;
94656758831SToomas Soome 	}
94756758831SToomas Soome 
948b9f745fdSToomas Soome 	/*
949b9f745fdSToomas Soome 	 * When we have serial port listed in ConOut, use pre-teken emulator,
950b9f745fdSToomas Soome 	 * if built with.
951b9f745fdSToomas Soome 	 * The problem is, we can not output text on efi and comconsole when
952b9f745fdSToomas Soome 	 * efi also has comconsole bound. But then again, we need to have
953b9f745fdSToomas Soome 	 * terminal emulator for efi text mode to support the menu.
954b9f745fdSToomas Soome 	 * While teken is too expensive to be used on serial console, the
955b9f745fdSToomas Soome 	 * pre-teken emulator is light enough to be used on serial console.
9563c5a4af6SWarner Losh 	 *
9573c5a4af6SWarner Losh 	 * When doing multiple consoles (both serial and video),
9583c5a4af6SWarner Losh 	 * also just use the old emulator. RB_MULTIPLE also implies
9593c5a4af6SWarner Losh 	 * we're using a serial console.
960b9f745fdSToomas Soome 	 */
961b9f745fdSToomas Soome 	mode = parse_uefi_con_out();
9623c5a4af6SWarner Losh 	if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) {
9633630506bSToomas Soome 		conout->EnableCursor(conout, FALSE);
9643630506bSToomas Soome 		gfx_state.tg_cursor_visible = false;
9653630506bSToomas Soome 
9663630506bSToomas Soome 		if (gfx_state.tg_fb_type == FB_TEXT) {
9673630506bSToomas Soome 
9683630506bSToomas Soome 			gfx_state.tg_functions = &tf;
9693630506bSToomas Soome 			/* ensure the following are not set for text mode */
9703630506bSToomas Soome 			unsetenv("screen.height");
9713630506bSToomas Soome 			unsetenv("screen.width");
9723630506bSToomas Soome 			unsetenv("screen.depth");
97356758831SToomas Soome 		} else {
9743630506bSToomas Soome 			uint32_t fb_height, fb_width;
9753630506bSToomas Soome 
9763630506bSToomas Soome 			fb_height = gfx_state.tg_fb.fb_height;
9773630506bSToomas Soome 			fb_width = gfx_state.tg_fb.fb_width;
9783630506bSToomas Soome 
9793630506bSToomas Soome 			/*
9803630506bSToomas Soome 			 * setup_font() can adjust terminal size.
981becaac39SToomas Soome 			 * We can see two kind of bad happening.
982becaac39SToomas Soome 			 * We either can get too small console font - requested
983becaac39SToomas Soome 			 * terminal size is large, display resolution is
984becaac39SToomas Soome 			 * large, and we get very small font.
985becaac39SToomas Soome 			 * Or, we can get too large font - requested
986becaac39SToomas Soome 			 * terminal size is small and this will cause large
987becaac39SToomas Soome 			 * font to be selected.
988becaac39SToomas Soome 			 * Now, the setup_font() is updated to consider
989becaac39SToomas Soome 			 * display density and this should give us mostly
990becaac39SToomas Soome 			 * acceptable font. However, the catch is, not all
991becaac39SToomas Soome 			 * display devices will give us display density.
992becaac39SToomas Soome 			 * Still, we do hope, external monitors do - this is
993becaac39SToomas Soome 			 * where the display size will matter the most.
994becaac39SToomas Soome 			 * And for laptop screens, we should still get good
995becaac39SToomas Soome 			 * results by requesting 80x25 terminal.
9963630506bSToomas Soome 			 */
997becaac39SToomas Soome 			gfx_state.tg_tp.tp_row = 25;
998becaac39SToomas Soome 			gfx_state.tg_tp.tp_col = 80;
9993630506bSToomas Soome 			setup_font(&gfx_state, fb_height, fb_width);
10003630506bSToomas Soome 			rows = gfx_state.tg_tp.tp_row;
10013630506bSToomas Soome 			cols = gfx_state.tg_tp.tp_col;
10023630506bSToomas Soome 			/* Point of origin in pixels. */
10033630506bSToomas Soome 			gfx_state.tg_origin.tp_row = (fb_height -
10043630506bSToomas Soome 			    (rows * gfx_state.tg_font.vf_height)) / 2;
10053630506bSToomas Soome 			gfx_state.tg_origin.tp_col = (fb_width -
10063630506bSToomas Soome 			    (cols * gfx_state.tg_font.vf_width)) / 2;
10073630506bSToomas Soome 
10083630506bSToomas Soome 			/* UEFI gop has depth 32. */
10093630506bSToomas Soome 			gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height *
10103630506bSToomas Soome 			    gfx_state.tg_font.vf_width * 4;
10113630506bSToomas Soome 			free(gfx_state.tg_glyph);
10123630506bSToomas Soome 			gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size);
10133630506bSToomas Soome 			if (gfx_state.tg_glyph == NULL)
10143630506bSToomas Soome 				return (false);
10153630506bSToomas Soome 
10163630506bSToomas Soome 			gfx_state.tg_functions = &tfx;
10173630506bSToomas Soome 			snprintf(env, sizeof (env), "%d", fb_height);
10183630506bSToomas Soome 			env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK,
10193630506bSToomas Soome 			    env, env_noset, env_screen_nounset);
10203630506bSToomas Soome 			snprintf(env, sizeof (env), "%d", fb_width);
10213630506bSToomas Soome 			env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK,
10223630506bSToomas Soome 			    env, env_noset, env_screen_nounset);
10233630506bSToomas Soome 			snprintf(env, sizeof (env), "%d",
10243630506bSToomas Soome 			    gfx_state.tg_fb.fb_bpp);
10253630506bSToomas Soome 			env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK,
10263630506bSToomas Soome 			    env, env_noset, env_screen_nounset);
102756758831SToomas Soome 		}
102856758831SToomas Soome 
10293630506bSToomas Soome 		/* Record our terminal screen size. */
10303630506bSToomas Soome 		gfx_state.tg_tp.tp_row = rows;
10313630506bSToomas Soome 		gfx_state.tg_tp.tp_col = cols;
10323630506bSToomas Soome 
10333630506bSToomas Soome 		teken_init(&gfx_state.tg_teken, gfx_state.tg_functions,
10343630506bSToomas Soome 		    &gfx_state);
10353630506bSToomas Soome 
10363630506bSToomas Soome 		free(screen_buffer);
10373630506bSToomas Soome 		screen_buffer = malloc(rows * cols * sizeof(*screen_buffer));
10383630506bSToomas Soome 		if (screen_buffer != NULL) {
10393630506bSToomas Soome 			teken_set_winsize(&gfx_state.tg_teken,
10403630506bSToomas Soome 			    &gfx_state.tg_tp);
10413630506bSToomas Soome 			a = teken_get_defattr(&gfx_state.tg_teken);
1042a536ed41SToomas Soome 			attr = *a;
104356758831SToomas Soome 
1044a536ed41SToomas Soome 			/*
1045a536ed41SToomas Soome 			 * On first run, we set up the efi_set_colors()
1046a536ed41SToomas Soome 			 * callback. If the env is already set, we
1047a536ed41SToomas Soome 			 * pick up fg and bg color values from the environment.
1048a536ed41SToomas Soome 			 */
1049a536ed41SToomas Soome 			ptr = getenv("teken.fg_color");
1050a536ed41SToomas Soome 			if (ptr != NULL) {
1051a536ed41SToomas Soome 				attr.ta_fgcolor = strtol(ptr, NULL, 10);
1052a536ed41SToomas Soome 				ptr = getenv("teken.bg_color");
1053a536ed41SToomas Soome 				attr.ta_bgcolor = strtol(ptr, NULL, 10);
1054a536ed41SToomas Soome 
10553630506bSToomas Soome 				teken_set_defattr(&gfx_state.tg_teken, &attr);
1056a536ed41SToomas Soome 			} else {
1057a536ed41SToomas Soome 				snprintf(env, sizeof(env), "%d",
1058a536ed41SToomas Soome 				    attr.ta_fgcolor);
105934edaae6SToomas Soome 				env_setenv("teken.fg_color", EV_VOLATILE, env,
106034edaae6SToomas Soome 				    efi_set_colors, env_nounset);
1061a536ed41SToomas Soome 				snprintf(env, sizeof(env), "%d",
1062a536ed41SToomas Soome 				    attr.ta_bgcolor);
106334edaae6SToomas Soome 				env_setenv("teken.bg_color", EV_VOLATILE, env,
106434edaae6SToomas Soome 				    efi_set_colors, env_nounset);
1065a536ed41SToomas Soome 			}
106634edaae6SToomas Soome 		}
106734edaae6SToomas Soome 	}
106834edaae6SToomas Soome 
10693630506bSToomas Soome 	if (screen_buffer == NULL) {
10703630506bSToomas Soome 		conout->EnableCursor(conout, TRUE);
1071b9f745fdSToomas Soome #ifdef TERM_EMU
1072b9f745fdSToomas Soome 		conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
1073b9f745fdSToomas Soome 		    DEFAULT_BGCOLOR));
1074b9f745fdSToomas Soome 		end_term();
1075b9f745fdSToomas Soome 		get_pos(&curx, &cury);
1076b9f745fdSToomas Soome 		curs_move(&curx, &cury, curx, cury);
1077b9f745fdSToomas Soome 		fg_c = DEFAULT_FGCOLOR;
1078b9f745fdSToomas Soome 		bg_c = DEFAULT_BGCOLOR;
107934edaae6SToomas Soome #endif
10803630506bSToomas Soome 	} else {
10813630506bSToomas Soome 		/* Improve visibility */
10823630506bSToomas Soome 		if (attr.ta_bgcolor == TC_WHITE)
10833630506bSToomas Soome 			attr.ta_bgcolor |= TC_LIGHT;
10843630506bSToomas Soome 		teken_set_defattr(&gfx_state.tg_teken, &attr);
10853630506bSToomas Soome 
10863630506bSToomas Soome 		/* Draw frame around terminal area. */
10873630506bSToomas Soome 		cons_draw_frame(&attr);
10883630506bSToomas Soome 		/*
10893630506bSToomas Soome 		 * Erase display, this will also fill our screen
10903630506bSToomas Soome 		 * buffer.
10913630506bSToomas Soome 		 */
10923630506bSToomas Soome 		teken_input(&gfx_state.tg_teken, "\e[2J", 4);
10933630506bSToomas Soome 		gfx_state.tg_functions->tf_param(&gfx_state,
10943630506bSToomas Soome 		    TP_SHOWCURSOR, 1);
10953630506bSToomas Soome 	}
109656758831SToomas Soome 
109756758831SToomas Soome 	snprintf(env, sizeof (env), "%u", (unsigned)rows);
109856758831SToomas Soome 	setenv("LINES", env, 1);
109956758831SToomas Soome 	snprintf(env, sizeof (env), "%u", (unsigned)cols);
110056758831SToomas Soome 	setenv("COLUMNS", env, 1);
110156758831SToomas Soome 
110256758831SToomas Soome 	return (true);
110356758831SToomas Soome }
110456758831SToomas Soome 
1105ca987d46SWarner Losh static int
1106ca987d46SWarner Losh efi_cons_init(int arg)
1107ca987d46SWarner Losh {
110805b24e86SToomas Soome 	EFI_STATUS status;
110905b24e86SToomas Soome 
11103630506bSToomas Soome 	if (efi_started)
11113630506bSToomas Soome 		return (0);
11123630506bSToomas Soome 
11133630506bSToomas Soome 	efi_started = true;
11143630506bSToomas Soome 
11153630506bSToomas Soome 	gfx_framework_init();
11163630506bSToomas Soome 	if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT))
111705b24e86SToomas Soome 		return (0);
1118ca987d46SWarner Losh 
111956758831SToomas Soome 	return (1);
1120ca987d46SWarner Losh }
1121ca987d46SWarner Losh 
1122b9f745fdSToomas Soome static void
1123b9f745fdSToomas Soome input_partial(void)
1124b9f745fdSToomas Soome {
1125b9f745fdSToomas Soome 	unsigned i;
1126b9f745fdSToomas Soome 	uint32_t c;
1127b9f745fdSToomas Soome 
1128b9f745fdSToomas Soome 	if (utf8_left == 0)
1129b9f745fdSToomas Soome 		return;
1130b9f745fdSToomas Soome 
1131b9f745fdSToomas Soome 	for (i = 0; i < sizeof(utf8_partial); i++) {
1132b9f745fdSToomas Soome 		c = (utf8_partial >> (24 - (i << 3))) & 0xff;
1133b9f745fdSToomas Soome 		if (c != 0)
1134b9f745fdSToomas Soome 			efi_term_emu(c);
1135b9f745fdSToomas Soome 	}
1136b9f745fdSToomas Soome 	utf8_left = 0;
1137b9f745fdSToomas Soome 	utf8_partial = 0;
1138b9f745fdSToomas Soome }
1139b9f745fdSToomas Soome 
1140b9f745fdSToomas Soome static void
1141b9f745fdSToomas Soome input_byte(uint8_t c)
1142b9f745fdSToomas Soome {
1143b9f745fdSToomas Soome 	if ((c & 0x80) == 0x00) {
1144b9f745fdSToomas Soome 		/* One-byte sequence. */
1145b9f745fdSToomas Soome 		input_partial();
1146b9f745fdSToomas Soome 		efi_term_emu(c);
1147b9f745fdSToomas Soome 		return;
1148b9f745fdSToomas Soome 	}
1149b9f745fdSToomas Soome 	if ((c & 0xe0) == 0xc0) {
1150b9f745fdSToomas Soome 		/* Two-byte sequence. */
1151b9f745fdSToomas Soome 		input_partial();
1152b9f745fdSToomas Soome 		utf8_left = 1;
1153b9f745fdSToomas Soome 		utf8_partial = c;
1154b9f745fdSToomas Soome 		return;
1155b9f745fdSToomas Soome 	}
1156b9f745fdSToomas Soome 	if ((c & 0xf0) == 0xe0) {
1157b9f745fdSToomas Soome 		/* Three-byte sequence. */
1158b9f745fdSToomas Soome 		input_partial();
1159b9f745fdSToomas Soome 		utf8_left = 2;
1160b9f745fdSToomas Soome 		utf8_partial = c;
1161b9f745fdSToomas Soome 		return;
1162b9f745fdSToomas Soome 	}
1163b9f745fdSToomas Soome 	if ((c & 0xf8) == 0xf0) {
1164b9f745fdSToomas Soome 		/* Four-byte sequence. */
1165b9f745fdSToomas Soome 		input_partial();
1166b9f745fdSToomas Soome 		utf8_left = 3;
1167b9f745fdSToomas Soome 		utf8_partial = c;
1168b9f745fdSToomas Soome 		return;
1169b9f745fdSToomas Soome 	}
1170b9f745fdSToomas Soome 	if ((c & 0xc0) == 0x80) {
1171b9f745fdSToomas Soome 		/* Invalid state? */
1172b9f745fdSToomas Soome 		if (utf8_left == 0) {
1173b9f745fdSToomas Soome 			efi_term_emu(c);
1174b9f745fdSToomas Soome 			return;
1175b9f745fdSToomas Soome 		}
1176b9f745fdSToomas Soome 		utf8_left--;
1177b9f745fdSToomas Soome 		utf8_partial = (utf8_partial << 8) | c;
1178b9f745fdSToomas Soome 		if (utf8_left == 0) {
1179b9f745fdSToomas Soome 			uint32_t v, u;
1180b9f745fdSToomas Soome 			uint8_t b;
1181b9f745fdSToomas Soome 
1182b9f745fdSToomas Soome 			v = 0;
1183b9f745fdSToomas Soome 			u = utf8_partial;
1184b9f745fdSToomas Soome 			b = (u >> 24) & 0xff;
1185b9f745fdSToomas Soome 			if (b != 0) {		/* Four-byte sequence */
1186b9f745fdSToomas Soome 				v = b & 0x07;
1187b9f745fdSToomas Soome 				b = (u >> 16) & 0xff;
1188b9f745fdSToomas Soome 				v = (v << 6) | (b & 0x3f);
1189b9f745fdSToomas Soome 				b = (u >> 8) & 0xff;
1190b9f745fdSToomas Soome 				v = (v << 6) | (b & 0x3f);
1191b9f745fdSToomas Soome 				b = u & 0xff;
1192b9f745fdSToomas Soome 				v = (v << 6) | (b & 0x3f);
1193b9f745fdSToomas Soome 			} else if ((b = (u >> 16) & 0xff) != 0) {
1194b9f745fdSToomas Soome 				v = b & 0x0f;	/* Three-byte sequence */
1195b9f745fdSToomas Soome 				b = (u >> 8) & 0xff;
1196b9f745fdSToomas Soome 				v = (v << 6) | (b & 0x3f);
1197b9f745fdSToomas Soome 				b = u & 0xff;
1198b9f745fdSToomas Soome 				v = (v << 6) | (b & 0x3f);
1199b9f745fdSToomas Soome 			} else if ((b = (u >> 8) & 0xff) != 0) {
1200b9f745fdSToomas Soome 				v = b & 0x1f;	/* Two-byte sequence */
1201b9f745fdSToomas Soome 				b = u & 0xff;
1202b9f745fdSToomas Soome 				v = (v << 6) | (b & 0x3f);
1203b9f745fdSToomas Soome 			}
1204b9f745fdSToomas Soome 			/* Send unicode char directly to console. */
1205b9f745fdSToomas Soome 			efi_cons_efiputchar(v);
1206b9f745fdSToomas Soome 			utf8_partial = 0;
1207b9f745fdSToomas Soome 		}
1208b9f745fdSToomas Soome 		return;
1209b9f745fdSToomas Soome 	}
1210b9f745fdSToomas Soome 	/* Anything left is illegal in UTF-8 sequence. */
1211b9f745fdSToomas Soome 	input_partial();
1212b9f745fdSToomas Soome 	efi_term_emu(c);
1213b9f745fdSToomas Soome }
1214b9f745fdSToomas Soome 
1215ca987d46SWarner Losh void
1216ca987d46SWarner Losh efi_cons_putchar(int c)
1217ca987d46SWarner Losh {
121856758831SToomas Soome 	unsigned char ch = c;
121956758831SToomas Soome 
12203c5a4af6SWarner Losh 	/*
12213c5a4af6SWarner Losh 	 * Don't use Teken when we're doing pure serial, or a multiple console
12223c5a4af6SWarner Losh 	 * with video "primary" because that's also serial.
12233c5a4af6SWarner Losh 	 */
12243630506bSToomas Soome 	if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) {
1225b9f745fdSToomas Soome 		input_byte(ch);
1226b9f745fdSToomas Soome 		return;
1227b9f745fdSToomas Soome 	}
1228b9f745fdSToomas Soome 
12293630506bSToomas Soome 	teken_input(&gfx_state.tg_teken, &ch, sizeof (ch));
1230ca987d46SWarner Losh }
1231ca987d46SWarner Losh 
1232fb0df666SToomas Soome static int
1233fb0df666SToomas Soome keybuf_getchar(void)
1234ca987d46SWarner Losh {
1235fb0df666SToomas Soome 	int i, c = 0;
1236ca987d46SWarner Losh 
1237fb0df666SToomas Soome 	for (i = 0; i < KEYBUFSZ; i++) {
1238fb0df666SToomas Soome 		if (keybuf[i] != 0) {
1239fb0df666SToomas Soome 			c = keybuf[i];
1240fb0df666SToomas Soome 			keybuf[i] = 0;
1241fb0df666SToomas Soome 			break;
1242ca987d46SWarner Losh 		}
1243ca987d46SWarner Losh 	}
1244ca987d46SWarner Losh 
1245fb0df666SToomas Soome 	return (c);
1246ca987d46SWarner Losh }
1247ca987d46SWarner Losh 
1248fb0df666SToomas Soome static bool
1249fb0df666SToomas Soome keybuf_ischar(void)
1250ca987d46SWarner Losh {
1251fb0df666SToomas Soome 	int i;
1252ca987d46SWarner Losh 
1253fb0df666SToomas Soome 	for (i = 0; i < KEYBUFSZ; i++) {
1254fb0df666SToomas Soome 		if (keybuf[i] != 0)
1255fb0df666SToomas Soome 			return (true);
1256fb0df666SToomas Soome 	}
1257fb0df666SToomas Soome 	return (false);
1258fb0df666SToomas Soome }
1259fb0df666SToomas Soome 
1260fb0df666SToomas Soome /*
1261fb0df666SToomas Soome  * We are not reading input before keybuf is empty, so we are safe
1262fb0df666SToomas Soome  * just to fill keybuf from the beginning.
1263fb0df666SToomas Soome  */
1264fb0df666SToomas Soome static void
1265fb0df666SToomas Soome keybuf_inschar(EFI_INPUT_KEY *key)
1266fb0df666SToomas Soome {
1267fb0df666SToomas Soome 
1268fb0df666SToomas Soome 	switch (key->ScanCode) {
1269591d5f0eSToomas Soome 	case SCAN_UP: /* UP */
1270fb0df666SToomas Soome 		keybuf[0] = 0x1b;	/* esc */
1271fb0df666SToomas Soome 		keybuf[1] = '[';
1272fb0df666SToomas Soome 		keybuf[2] = 'A';
1273fb0df666SToomas Soome 		break;
1274591d5f0eSToomas Soome 	case SCAN_DOWN: /* DOWN */
1275fb0df666SToomas Soome 		keybuf[0] = 0x1b;	/* esc */
1276fb0df666SToomas Soome 		keybuf[1] = '[';
1277fb0df666SToomas Soome 		keybuf[2] = 'B';
1278fb0df666SToomas Soome 		break;
1279591d5f0eSToomas Soome 	case SCAN_RIGHT: /* RIGHT */
1280fb0df666SToomas Soome 		keybuf[0] = 0x1b;	/* esc */
1281fb0df666SToomas Soome 		keybuf[1] = '[';
1282fb0df666SToomas Soome 		keybuf[2] = 'C';
1283fb0df666SToomas Soome 		break;
1284591d5f0eSToomas Soome 	case SCAN_LEFT: /* LEFT */
1285fb0df666SToomas Soome 		keybuf[0] = 0x1b;	/* esc */
1286fb0df666SToomas Soome 		keybuf[1] = '[';
1287fb0df666SToomas Soome 		keybuf[2] = 'D';
1288fb0df666SToomas Soome 		break;
1289591d5f0eSToomas Soome 	case SCAN_DELETE:
1290591d5f0eSToomas Soome 		keybuf[0] = CHAR_BACKSPACE;
1291591d5f0eSToomas Soome 		break;
1292591d5f0eSToomas Soome 	case SCAN_ESC:
1293fb0df666SToomas Soome 		keybuf[0] = 0x1b;	/* esc */
1294fb0df666SToomas Soome 		break;
1295fb0df666SToomas Soome 	default:
1296fb0df666SToomas Soome 		keybuf[0] = key->UnicodeChar;
1297fb0df666SToomas Soome 		break;
1298fb0df666SToomas Soome 	}
1299fb0df666SToomas Soome }
1300fb0df666SToomas Soome 
1301fb0df666SToomas Soome static bool
1302fb0df666SToomas Soome efi_readkey(void)
1303fb0df666SToomas Soome {
1304fb0df666SToomas Soome 	EFI_STATUS status;
1305fb0df666SToomas Soome 	EFI_INPUT_KEY key;
1306fb0df666SToomas Soome 
1307ca987d46SWarner Losh 	status = conin->ReadKeyStroke(conin, &key);
1308ca987d46SWarner Losh 	if (status == EFI_SUCCESS) {
1309fb0df666SToomas Soome 		keybuf_inschar(&key);
1310fb0df666SToomas Soome 		return (true);
1311ca987d46SWarner Losh 	}
1312fb0df666SToomas Soome 	return (false);
1313ca987d46SWarner Losh }
1314ca987d46SWarner Losh 
131505b24e86SToomas Soome static bool
131605b24e86SToomas Soome efi_readkey_ex(void)
131705b24e86SToomas Soome {
131805b24e86SToomas Soome 	EFI_STATUS status;
131905b24e86SToomas Soome 	EFI_INPUT_KEY *kp;
132005b24e86SToomas Soome 	EFI_KEY_DATA  key_data;
132105b24e86SToomas Soome 	uint32_t kss;
132205b24e86SToomas Soome 
132305b24e86SToomas Soome 	status = coninex->ReadKeyStrokeEx(coninex, &key_data);
132405b24e86SToomas Soome 	if (status == EFI_SUCCESS) {
132505b24e86SToomas Soome 		kss = key_data.KeyState.KeyShiftState;
132605b24e86SToomas Soome 		kp = &key_data.Key;
132705b24e86SToomas Soome 		if (kss & EFI_SHIFT_STATE_VALID) {
132805b24e86SToomas Soome 
132905b24e86SToomas Soome 			/*
133005b24e86SToomas Soome 			 * quick mapping to control chars, replace with
133105b24e86SToomas Soome 			 * map lookup later.
133205b24e86SToomas Soome 			 */
133305b24e86SToomas Soome 			if (kss & EFI_RIGHT_CONTROL_PRESSED ||
133405b24e86SToomas Soome 			    kss & EFI_LEFT_CONTROL_PRESSED) {
133505b24e86SToomas Soome 				if (kp->UnicodeChar >= 'a' &&
133605b24e86SToomas Soome 				    kp->UnicodeChar <= 'z') {
133705b24e86SToomas Soome 					kp->UnicodeChar -= 'a';
133805b24e86SToomas Soome 					kp->UnicodeChar++;
133905b24e86SToomas Soome 				}
134005b24e86SToomas Soome 			}
1341ade8a0f1SToomas Soome 		}
1342ade8a0f1SToomas Soome 		/*
1343ade8a0f1SToomas Soome 		 * The shift state and/or toggle state may not be valid,
1344ade8a0f1SToomas Soome 		 * but we still can have ScanCode or UnicodeChar.
1345ade8a0f1SToomas Soome 		 */
13460aff5f39SToomas Soome 		if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
13470aff5f39SToomas Soome 			return (false);
134805b24e86SToomas Soome 		keybuf_inschar(kp);
134905b24e86SToomas Soome 		return (true);
135005b24e86SToomas Soome 	}
135105b24e86SToomas Soome 	return (false);
135205b24e86SToomas Soome }
135305b24e86SToomas Soome 
1354fb0df666SToomas Soome int
1355fb0df666SToomas Soome efi_cons_getchar(void)
1356fb0df666SToomas Soome {
1357fb0df666SToomas Soome 	int c;
1358fb0df666SToomas Soome 
1359fb0df666SToomas Soome 	if ((c = keybuf_getchar()) != 0)
1360fb0df666SToomas Soome 		return (c);
1361fb0df666SToomas Soome 
1362a2e02d9dSToomas Soome 	if (!boot_services_active)
1363a2e02d9dSToomas Soome 		return (-1);
1364a2e02d9dSToomas Soome 
1365fb0df666SToomas Soome 	key_pending = 0;
1366fb0df666SToomas Soome 
136705b24e86SToomas Soome 	if (coninex == NULL) {
1368fb0df666SToomas Soome 		if (efi_readkey())
1369fb0df666SToomas Soome 			return (keybuf_getchar());
137005b24e86SToomas Soome 	} else {
137105b24e86SToomas Soome 		if (efi_readkey_ex())
137205b24e86SToomas Soome 			return (keybuf_getchar());
137305b24e86SToomas Soome 	}
1374fb0df666SToomas Soome 
1375fb0df666SToomas Soome 	return (-1);
1376fb0df666SToomas Soome }
1377fb0df666SToomas Soome 
1378fb0df666SToomas Soome int
1379fb0df666SToomas Soome efi_cons_poll(void)
1380fb0df666SToomas Soome {
138105b24e86SToomas Soome 	EFI_STATUS status;
1382fb0df666SToomas Soome 
1383fb0df666SToomas Soome 	if (keybuf_ischar() || key_pending)
1384fb0df666SToomas Soome 		return (1);
1385fb0df666SToomas Soome 
1386a2e02d9dSToomas Soome 	if (!boot_services_active)
1387a2e02d9dSToomas Soome 		return (0);
1388a2e02d9dSToomas Soome 
1389fb0df666SToomas Soome 	/*
1390fb0df666SToomas Soome 	 * Some EFI implementation (u-boot for example) do not support
1391fb0df666SToomas Soome 	 * WaitForKey().
1392fb0df666SToomas Soome 	 * CheckEvent() can clear the signaled state.
1393fb0df666SToomas Soome 	 */
139405b24e86SToomas Soome 	if (coninex != NULL) {
139505b24e86SToomas Soome 		if (coninex->WaitForKeyEx == NULL) {
139605b24e86SToomas Soome 			key_pending = efi_readkey_ex();
139705b24e86SToomas Soome 		} else {
139805b24e86SToomas Soome 			status = BS->CheckEvent(coninex->WaitForKeyEx);
139905b24e86SToomas Soome 			key_pending = status == EFI_SUCCESS;
140005b24e86SToomas Soome 		}
140105b24e86SToomas Soome 	} else {
140205b24e86SToomas Soome 		if (conin->WaitForKey == NULL) {
1403fb0df666SToomas Soome 			key_pending = efi_readkey();
140405b24e86SToomas Soome 		} else {
140505b24e86SToomas Soome 			status = BS->CheckEvent(conin->WaitForKey);
140605b24e86SToomas Soome 			key_pending = status == EFI_SUCCESS;
140705b24e86SToomas Soome 		}
140805b24e86SToomas Soome 	}
1409fb0df666SToomas Soome 
1410fb0df666SToomas Soome 	return (key_pending);
1411ca987d46SWarner Losh }
1412ca987d46SWarner Losh 
1413ca987d46SWarner Losh /* Plain direct access to EFI OutputString(). */
1414ca987d46SWarner Losh void
1415ca987d46SWarner Losh efi_cons_efiputchar(int c)
1416ca987d46SWarner Losh {
1417ca987d46SWarner Losh 	CHAR16 buf[2];
141856758831SToomas Soome 	EFI_STATUS status;
1419ca987d46SWarner Losh 
1420ca987d46SWarner Losh 	buf[0] = c;
1421ca987d46SWarner Losh         buf[1] = 0;     /* terminate string */
1422ca987d46SWarner Losh 
142356758831SToomas Soome 	status = conout->TestString(conout, buf);
142456758831SToomas Soome 	if (EFI_ERROR(status))
142556758831SToomas Soome 		buf[0] = '?';
1426ca987d46SWarner Losh 	conout->OutputString(conout, buf);
1427ca987d46SWarner Losh }
1428