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