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
40extern EFI_GUID gop_guid;
41static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
42static SIMPLE_TEXT_OUTPUT_INTERFACE	*conout;
43static SIMPLE_INPUT_INTERFACE		*conin;
44static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
45static bool efi_started;
46
47static int mode;		/* Does ConOut have serial console? */
48
49static uint32_t utf8_left;
50static 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
56static int args[MAXARGS], argc;
57static int fg_c, bg_c, curx, cury;
58static int esc;
59
60void get_pos(int *x, int *y);
61void curs_move(int *_x, int *_y, int x, int y);
62static void CL(int);
63void HO(void);
64void end_term(void);
65#endif
66
67#define	TEXT_ROWS	24
68#define	TEXT_COLS	80
69
70static tf_bell_t	efi_cons_bell;
71static tf_cursor_t	efi_text_cursor;
72static tf_putchar_t	efi_text_putchar;
73static tf_fill_t	efi_text_fill;
74static tf_copy_t	efi_text_copy;
75static tf_param_t	efi_text_param;
76static tf_respond_t	efi_cons_respond;
77
78static 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
88static 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
99static unsigned keybuf[KEYBUFSZ];	/* keybuf for extended codes */
100static int key_pending;
101
102static 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
121static void efi_cons_probe(struct console *);
122static int efi_cons_init(int);
123void efi_cons_putchar(int);
124int efi_cons_getchar(void);
125void efi_cons_efiputchar(int);
126int efi_cons_poll(void);
127static void cons_draw_frame(teken_attr_t *);
128
129struct 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 */
144void
145term_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 */
168static void
169efi_cons_bell(void *s __unused)
170{
171}
172
173static void
174efi_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
190static void
191efi_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
229static void
230efi_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
247static void
248efi_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
265static void
266efi_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
297static void
298efi_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
354static void
355efi_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 */
387static void
388efi_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 */
396static void
397efi_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
420static bool
421color_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
458static int
459efi_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. */
514void
515get_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). */
522void
523curs_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. */
533void
534end_term(void)
535{
536	esc = 0;
537	argc = -1;
538}
539#endif
540
541static void
542efi_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. */
598static void
599bail_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. */
620static void
621CD(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 */
647static void
648CM(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. */
659void
660HO(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 */
668static void
669CL(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
712static void
713get_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 */
723static void
724efi_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
846static int
847env_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
854static void
855cons_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
880bool
881cons_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
1079static int
1080efi_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
1096static void
1097input_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
1114static void
1115input_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
1189void
1190efi_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
1206static int
1207keybuf_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
1222static bool
1223keybuf_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 */
1238static void
1239keybuf_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
1275static bool
1276efi_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
1289static bool
1290efi_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
1328int
1329efi_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
1349int
1350efi_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(). */
1382void
1383efi_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