xref: /freebsd/stand/efi/libefi/efi_console.c (revision d6eb98610fa65663bf0df4574b7cb2c5c4ffda71)
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 <efi.h>
31 #include <efilib.h>
32 
33 #include "bootstrap.h"
34 
35 static SIMPLE_TEXT_OUTPUT_INTERFACE	*conout;
36 static SIMPLE_INPUT_INTERFACE		*conin;
37 
38 #ifdef TERM_EMU
39 #define	DEFAULT_FGCOLOR	EFI_LIGHTGRAY
40 #define	DEFAULT_BGCOLOR	EFI_BLACK
41 
42 #define	MAXARGS	8
43 static int args[MAXARGS], argc;
44 static int fg_c, bg_c, curx, cury;
45 static int esc;
46 
47 void get_pos(int *x, int *y);
48 void curs_move(int *_x, int *_y, int x, int y);
49 static void CL(int);
50 void HO(void);
51 void end_term(void);
52 #endif
53 
54 #define	KEYBUFSZ 10
55 static unsigned keybuf[KEYBUFSZ];	/* keybuf for extended codes */
56 static int key_pending;
57 
58 static void efi_cons_probe(struct console *);
59 static int efi_cons_init(int);
60 void efi_cons_putchar(int);
61 int efi_cons_getchar(void);
62 void efi_cons_efiputchar(int);
63 int efi_cons_poll(void);
64 
65 struct console efi_console = {
66 	"efi",
67 	"EFI console",
68 	C_WIDEOUT,
69 	efi_cons_probe,
70 	efi_cons_init,
71 	efi_cons_putchar,
72 	efi_cons_getchar,
73 	efi_cons_poll
74 };
75 
76 #ifdef TERM_EMU
77 
78 /* Get cursor position. */
79 void
80 get_pos(int *x, int *y)
81 {
82 	*x = conout->Mode->CursorColumn;
83 	*y = conout->Mode->CursorRow;
84 }
85 
86 /* Move cursor to x rows and y cols (0-based). */
87 void
88 curs_move(int *_x, int *_y, int x, int y)
89 {
90 	conout->SetCursorPosition(conout, x, y);
91 	if (_x != NULL)
92 		*_x = conout->Mode->CursorColumn;
93 	if (_y != NULL)
94 		*_y = conout->Mode->CursorRow;
95 }
96 
97 /* Clear internal state of the terminal emulation code. */
98 void
99 end_term(void)
100 {
101 	esc = 0;
102 	argc = -1;
103 }
104 
105 #endif
106 
107 static void
108 efi_cons_probe(struct console *cp)
109 {
110 	conout = ST->ConOut;
111 	conin = ST->ConIn;
112 	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
113 }
114 
115 static int
116 efi_cons_init(int arg)
117 {
118 #ifdef TERM_EMU
119 	conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
120 	    DEFAULT_BGCOLOR));
121 	end_term();
122 	get_pos(&curx, &cury);
123 	curs_move(&curx, &cury, curx, cury);
124 	fg_c = DEFAULT_FGCOLOR;
125 	bg_c = DEFAULT_BGCOLOR;
126 #endif
127 	conout->EnableCursor(conout, TRUE);
128 	return 0;
129 }
130 
131 static void
132 efi_cons_rawputchar(int c)
133 {
134 	int i;
135 	UINTN x, y;
136 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
137 
138 	if (c == '\t') {
139 		int n;
140 
141 		n = 8 - ((conout->Mode->CursorColumn + 8) % 8);
142 		for (i = 0; i < n; i++)
143 			efi_cons_rawputchar(' ');
144 	} else {
145 #ifndef	TERM_EMU
146 		if (c == '\n')
147 			efi_cons_efiputchar('\r');
148 		efi_cons_efiputchar(c);
149 #else
150 		switch (c) {
151 		case '\r':
152 			curx = 0;
153 			efi_cons_efiputchar('\r');
154 			return;
155 		case '\n':
156 			efi_cons_efiputchar('\n');
157 			efi_cons_efiputchar('\r');
158 			cury++;
159 			if (cury >= y)
160 				cury--;
161 			curx = 0;
162 			return;
163 		case '\b':
164 			if (curx > 0) {
165 				efi_cons_efiputchar('\b');
166 				curx--;
167 			}
168 			return;
169 		default:
170 			efi_cons_efiputchar(c);
171 			curx++;
172 			if (curx > x-1) {
173 				curx = 0;
174 				cury++;
175 			}
176 			if (cury > y-1) {
177 				curx = 0;
178 				cury--;
179 			}
180 		}
181 #endif
182 	}
183 }
184 
185 #ifdef TERM_EMU
186 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
187 static void
188 bail_out(int c)
189 {
190 	char buf[16], *ch;
191 	int i;
192 
193 	if (esc) {
194 		efi_cons_rawputchar('\033');
195 		if (esc != '\033')
196 			efi_cons_rawputchar(esc);
197 		for (i = 0; i <= argc; ++i) {
198 			sprintf(buf, "%d", args[i]);
199 			ch = buf;
200 			while (*ch)
201 				efi_cons_rawputchar(*ch++);
202 		}
203 	}
204 	efi_cons_rawputchar(c);
205 	end_term();
206 }
207 
208 /* Clear display from current position to end of screen. */
209 static void
210 CD(void) {
211 	int i;
212 	UINTN x, y;
213 
214 	get_pos(&curx, &cury);
215 	if (curx == 0 && cury == 0) {
216 		conout->ClearScreen(conout);
217 		end_term();
218 		return;
219 	}
220 
221 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
222 	CL(0);  /* clear current line from cursor to end */
223 	for (i = cury + 1; i < y-1; i++) {
224 		curs_move(NULL, NULL, 0, i);
225 		CL(0);
226 	}
227 	curs_move(NULL, NULL, curx, cury);
228 	end_term();
229 }
230 
231 /*
232  * Absolute cursor move to args[0] rows and args[1] columns
233  * (the coordinates are 1-based).
234  */
235 static void
236 CM(void)
237 {
238 	if (args[0] > 0)
239 		args[0]--;
240 	if (args[1] > 0)
241 		args[1]--;
242 	curs_move(&curx, &cury, args[1], args[0]);
243 	end_term();
244 }
245 
246 /* Home cursor (left top corner), also called from mode command. */
247 void
248 HO(void)
249 {
250 	argc = 1;
251 	args[0] = args[1] = 1;
252 	CM();
253 }
254 
255 /* Clear line from current position to end of line */
256 static void
257 CL(int direction)
258 {
259 	int i, len;
260 	UINTN x, y;
261 	CHAR16 *line;
262 
263 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
264 	switch (direction) {
265 	case 0:         /* from cursor to end */
266 		len = x - curx + 1;
267 		break;
268 	case 1:         /* from beginning to cursor */
269 		len = curx;
270 		break;
271 	case 2:         /* entire line */
272 		len = x;
273 		break;
274 	default:	/* NOTREACHED */
275 		__unreachable();
276 	}
277 
278 	if (cury == y - 1)
279 		len--;
280 
281 	line = malloc(len * sizeof (CHAR16));
282 	if (line == NULL) {
283 		printf("out of memory\n");
284 		return;
285 	}
286 	for (i = 0; i < len; i++)
287 		line[i] = ' ';
288 	line[len-1] = 0;
289 
290 	if (direction != 0)
291 		curs_move(NULL, NULL, 0, cury);
292 
293 	conout->OutputString(conout, line);
294 	/* restore cursor position */
295 	curs_move(NULL, NULL, curx, cury);
296 	free(line);
297 	end_term();
298 }
299 
300 static void
301 get_arg(int c)
302 {
303 	if (argc < 0)
304 		argc = 0;
305 	args[argc] *= 10;
306 	args[argc] += c - '0';
307 }
308 
309 /* Emulate basic capabilities of cons25 terminal */
310 static void
311 efi_term_emu(int c)
312 {
313 	static int ansi_col[] = {
314 		0, 4, 2, 6, 1, 5, 3, 7
315 	};
316 	int t, i;
317 
318 	switch (esc) {
319 	case 0:
320 		switch (c) {
321 		case '\033':
322 			esc = c;
323 			break;
324 		default:
325 			efi_cons_rawputchar(c);
326 			break;
327 		}
328 		break;
329 	case '\033':
330 		switch (c) {
331 		case '[':
332 			esc = c;
333 			args[0] = 0;
334 			argc = -1;
335 			break;
336 		default:
337 			bail_out(c);
338 			break;
339 		}
340 		break;
341 	case '[':
342 		switch (c) {
343 		case ';':
344 			if (argc < 0)
345 				argc = 0;
346 			else if (argc + 1 >= MAXARGS)
347 				bail_out(c);
348 			else
349 				args[++argc] = 0;
350 			break;
351 		case 'H':               /* ho = \E[H */
352 			if (argc < 0)
353 				HO();
354 			else if (argc == 1)
355 				CM();
356 			else
357 				bail_out(c);
358 			break;
359 		case 'J':               /* cd = \E[J */
360 			if (argc < 0)
361 				CD();
362 			else
363 				bail_out(c);
364 			break;
365 		case 'm':
366 			if (argc < 0) {
367 				fg_c = DEFAULT_FGCOLOR;
368 				bg_c = DEFAULT_BGCOLOR;
369 			}
370 			for (i = 0; i <= argc; ++i) {
371 				switch (args[i]) {
372 				case 0:         /* back to normal */
373 					fg_c = DEFAULT_FGCOLOR;
374 					bg_c = DEFAULT_BGCOLOR;
375 					break;
376 				case 1:         /* bold */
377 					fg_c |= 0x8;
378 					break;
379 				case 4:         /* underline */
380 				case 5:         /* blink */
381 					bg_c |= 0x8;
382 					break;
383 				case 7:         /* reverse */
384 					t = fg_c;
385 					fg_c = bg_c;
386 					bg_c = t;
387 					break;
388 				case 22:	/* normal intensity */
389 					fg_c &= ~0x8;
390 					break;
391 				case 24:	/* not underline */
392 				case 25:	/* not blinking */
393 					bg_c &= ~0x8;
394 					break;
395 				case 30: case 31: case 32: case 33:
396 				case 34: case 35: case 36: case 37:
397 					fg_c = ansi_col[args[i] - 30];
398 					break;
399 				case 39:        /* normal */
400 					fg_c = DEFAULT_FGCOLOR;
401 					break;
402 				case 40: case 41: case 42: case 43:
403 				case 44: case 45: case 46: case 47:
404 					bg_c = ansi_col[args[i] - 40];
405 					break;
406 				case 49:        /* normal */
407 					bg_c = DEFAULT_BGCOLOR;
408 					break;
409 				}
410 			}
411 			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
412 			end_term();
413 			break;
414 		default:
415 			if (isdigit(c))
416 				get_arg(c);
417 			else
418 				bail_out(c);
419 			break;
420 		}
421 		break;
422 	default:
423 		bail_out(c);
424 		break;
425 	}
426 }
427 #else
428 void
429 HO(void)
430 {
431 }
432 #endif
433 
434 void
435 efi_cons_putchar(int c)
436 {
437 #ifdef TERM_EMU
438 	efi_term_emu(c);
439 #else
440 	efi_cons_rawputchar(c);
441 #endif
442 }
443 
444 static int
445 keybuf_getchar(void)
446 {
447 	int i, c = 0;
448 
449 	for (i = 0; i < KEYBUFSZ; i++) {
450 		if (keybuf[i] != 0) {
451 			c = keybuf[i];
452 			keybuf[i] = 0;
453 			break;
454 		}
455 	}
456 
457 	return (c);
458 }
459 
460 static bool
461 keybuf_ischar(void)
462 {
463 	int i;
464 
465 	for (i = 0; i < KEYBUFSZ; i++) {
466 		if (keybuf[i] != 0)
467 			return (true);
468 	}
469 	return (false);
470 }
471 
472 /*
473  * We are not reading input before keybuf is empty, so we are safe
474  * just to fill keybuf from the beginning.
475  */
476 static void
477 keybuf_inschar(EFI_INPUT_KEY *key)
478 {
479 
480 	switch (key->ScanCode) {
481 	case 0x1: /* UP */
482 		keybuf[0] = 0x1b;	/* esc */
483 		keybuf[1] = '[';
484 		keybuf[2] = 'A';
485 		break;
486 	case 0x2: /* DOWN */
487 		keybuf[0] = 0x1b;	/* esc */
488 		keybuf[1] = '[';
489 		keybuf[2] = 'B';
490 		break;
491 	case 0x3: /* RIGHT */
492 		keybuf[0] = 0x1b;	/* esc */
493 		keybuf[1] = '[';
494 		keybuf[2] = 'C';
495 		break;
496 	case 0x4: /* LEFT */
497 		keybuf[0] = 0x1b;	/* esc */
498 		keybuf[1] = '[';
499 		keybuf[2] = 'D';
500 		break;
501 	case 0x17:
502 		keybuf[0] = 0x1b;	/* esc */
503 		break;
504 	default:
505 		keybuf[0] = key->UnicodeChar;
506 		break;
507 	}
508 }
509 
510 static bool
511 efi_readkey(void)
512 {
513 	EFI_STATUS status;
514 	EFI_INPUT_KEY key;
515 
516 	status = conin->ReadKeyStroke(conin, &key);
517 	if (status == EFI_SUCCESS) {
518 		keybuf_inschar(&key);
519 		return (true);
520 	}
521 	return (false);
522 }
523 
524 int
525 efi_cons_getchar(void)
526 {
527 	int c;
528 
529 	if ((c = keybuf_getchar()) != 0)
530 		return (c);
531 
532 	key_pending = 0;
533 
534 	if (efi_readkey())
535 		return (keybuf_getchar());
536 
537 	return (-1);
538 }
539 
540 int
541 efi_cons_poll(void)
542 {
543 
544 	if (keybuf_ischar() || key_pending)
545 		return (1);
546 
547 	/*
548 	 * Some EFI implementation (u-boot for example) do not support
549 	 * WaitForKey().
550 	 * CheckEvent() can clear the signaled state.
551 	 */
552 	if (conin->WaitForKey == NULL)
553 		key_pending = efi_readkey();
554 	else
555 		key_pending = BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS;
556 
557 	return (key_pending);
558 }
559 
560 /* Plain direct access to EFI OutputString(). */
561 void
562 efi_cons_efiputchar(int c)
563 {
564 	CHAR16 buf[2];
565 
566 	/*
567 	 * translate box chars to unicode
568 	 */
569 	switch (c) {
570 	/* single frame */
571 	case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
572 	case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
573 	case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
574 	case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
575 	case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
576 	case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
577 
578 	/* double frame */
579 	case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
580 	case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
581 	case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
582 	case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
583 	case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
584 	case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
585 
586 	default:
587 		buf[0] = c;
588 	}
589         buf[1] = 0;     /* terminate string */
590 
591 	conout->OutputString(conout, buf);
592 }
593