xref: /freebsd/stand/efi/libefi/efi_console.c (revision 780fb4a2)
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 static EFI_INPUT_KEY key_cur;
55 static int key_pending;
56 
57 static void efi_cons_probe(struct console *);
58 static int efi_cons_init(int);
59 void efi_cons_putchar(int);
60 int efi_cons_getchar(void);
61 void efi_cons_efiputchar(int);
62 int efi_cons_poll(void);
63 
64 struct console efi_console = {
65 	"efi",
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 };
74 
75 #ifdef TERM_EMU
76 
77 /* Get cursor position. */
78 void
79 get_pos(int *x, int *y)
80 {
81 	*x = conout->Mode->CursorColumn;
82 	*y = conout->Mode->CursorRow;
83 }
84 
85 /* Move cursor to x rows and y cols (0-based). */
86 void
87 curs_move(int *_x, int *_y, int x, int y)
88 {
89 	conout->SetCursorPosition(conout, x, y);
90 	if (_x != NULL)
91 		*_x = conout->Mode->CursorColumn;
92 	if (_y != NULL)
93 		*_y = conout->Mode->CursorRow;
94 }
95 
96 /* Clear internal state of the terminal emulation code. */
97 void
98 end_term(void)
99 {
100 	esc = 0;
101 	argc = -1;
102 }
103 
104 #endif
105 
106 static void
107 efi_cons_probe(struct console *cp)
108 {
109 	conout = ST->ConOut;
110 	conin = ST->ConIn;
111 	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
112 }
113 
114 static int
115 efi_cons_init(int arg)
116 {
117 #ifdef TERM_EMU
118 	conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
119 	    DEFAULT_BGCOLOR));
120 	end_term();
121 	get_pos(&curx, &cury);
122 	curs_move(&curx, &cury, curx, cury);
123 	fg_c = DEFAULT_FGCOLOR;
124 	bg_c = DEFAULT_BGCOLOR;
125 #endif
126 	conout->EnableCursor(conout, TRUE);
127 	return 0;
128 }
129 
130 static void
131 efi_cons_rawputchar(int c)
132 {
133 	int i;
134 	UINTN x, y;
135 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
136 
137 	if (c == '\t')
138 		/* XXX lame tab expansion */
139 		for (i = 0; i < 8; i++)
140 			efi_cons_rawputchar(' ');
141 	else {
142 #ifndef	TERM_EMU
143 		if (c == '\n')
144 			efi_cons_efiputchar('\r');
145 		efi_cons_efiputchar(c);
146 #else
147 		switch (c) {
148 		case '\r':
149 			curx = 0;
150 			curs_move(&curx, &cury, curx, cury);
151 			return;
152 		case '\n':
153 			cury++;
154 			if (cury >= y) {
155 				efi_cons_efiputchar('\n');
156 				cury--;
157 			} else
158 				curs_move(&curx, &cury, curx, cury);
159 			return;
160 		case '\b':
161 			if (curx > 0) {
162 				curx--;
163 				curs_move(&curx, &cury, curx, cury);
164 			}
165 			return;
166 		default:
167 			efi_cons_efiputchar(c);
168 			curx++;
169 			if (curx > x-1) {
170 				curx = 0;
171 				cury++;
172 			}
173 			if (cury > y-1) {
174 				curx = 0;
175 				cury--;
176 			}
177 		}
178 		curs_move(&curx, &cury, curx, cury);
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 int
443 efi_cons_getchar()
444 {
445 	EFI_INPUT_KEY key;
446 	EFI_STATUS status;
447 	UINTN junk;
448 
449 	if (key_pending) {
450 		key = key_cur;
451 		key_pending = 0;
452 	} else {
453 		/* Try to read a key stroke. We wait for one if none is pending. */
454 		status = conin->ReadKeyStroke(conin, &key);
455 		while (status == EFI_NOT_READY) {
456 			/* Some EFI implementation (u-boot for example) do not support WaitForKey */
457 			if (conin->WaitForKey != NULL)
458 				BS->WaitForEvent(1, &conin->WaitForKey, &junk);
459 			status = conin->ReadKeyStroke(conin, &key);
460 		}
461 	}
462 
463 	switch (key.ScanCode) {
464 	case 0x17: /* ESC */
465 		return (0x1b);  /* esc */
466 	}
467 
468 	/* this can return  */
469 	return (key.UnicodeChar);
470 }
471 
472 int
473 efi_cons_poll()
474 {
475 	EFI_INPUT_KEY key;
476 	EFI_STATUS status;
477 
478 	if (conin->WaitForKey == NULL) {
479 		if (key_pending)
480 			return (1);
481 		status = conin->ReadKeyStroke(conin, &key);
482 		if (status == EFI_SUCCESS) {
483 			key_cur = key;
484 			key_pending = 1;
485 		}
486 		return (key_pending);
487 	}
488 
489 	/* This can clear the signaled state. */
490 	return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
491 }
492 
493 /* Plain direct access to EFI OutputString(). */
494 void
495 efi_cons_efiputchar(int c)
496 {
497 	CHAR16 buf[2];
498 
499 	/*
500 	 * translate box chars to unicode
501 	 */
502 	switch (c) {
503 	/* single frame */
504 	case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
505 	case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
506 	case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
507 	case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
508 	case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
509 	case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
510 
511 	/* double frame */
512 	case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
513 	case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
514 	case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
515 	case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
516 	case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
517 	case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
518 
519 	default:
520 		buf[0] = c;
521 	}
522         buf[1] = 0;     /* terminate string */
523 
524 	conout->OutputString(conout, buf);
525 }
526