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