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