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