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