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