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