xref: /dragonfly/stand/boot/efi/libefi/efi_console.c (revision 7d3e9a5b)
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: head/sys/boot/efi/libefi/efi_console.c 293724 2016-01-12 02:17:39Z smh $");
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 void efi_cons_probe(struct console *);
55 static int efi_cons_init(int);
56 void efi_cons_putchar(int);
57 int efi_cons_getchar(void);
58 void efi_cons_efiputchar(int);
59 int efi_cons_poll(void);
60 
61 struct console efi_console = {
62 	"efi",
63 	"EFI console",
64 	0,
65 	efi_cons_probe,
66 	efi_cons_init,
67 	efi_cons_putchar,
68 	efi_cons_getchar,
69 	efi_cons_poll
70 };
71 
72 #ifdef TERM_EMU
73 
74 /* Get cursor position. */
75 void
76 get_pos(int *x, int *y)
77 {
78 	*x = conout->Mode->CursorColumn;
79 	*y = conout->Mode->CursorRow;
80 }
81 
82 /* Move cursor to x rows and y cols (0-based). */
83 void
84 curs_move(int *_x, int *_y, int x, int y)
85 {
86 	conout->SetCursorPosition(conout, x, y);
87 	if (_x != NULL)
88 		*_x = conout->Mode->CursorColumn;
89 	if (_y != NULL)
90 		*_y = conout->Mode->CursorRow;
91 }
92 
93 /* Clear internal state of the terminal emulation code. */
94 void
95 end_term(void)
96 {
97 	esc = 0;
98 	argc = -1;
99 }
100 
101 #endif
102 
103 static void
104 efi_cons_probe(struct console *cp)
105 {
106 	conout = ST->ConOut;
107 	conin = ST->ConIn;
108 	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
109 }
110 
111 static int
112 efi_cons_init(int arg)
113 {
114 	conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
115 	    DEFAULT_BGCOLOR));
116 #ifdef TERM_EMU
117 	end_term();
118 	get_pos(&curx, &cury);
119 	curs_move(&curx, &cury, curx, cury);
120 	fg_c = DEFAULT_FGCOLOR;
121 	bg_c = DEFAULT_BGCOLOR;
122 #endif
123 	conout->EnableCursor(conout, TRUE);
124 	return 0;
125 }
126 
127 static void
128 efi_cons_rawputchar(int c)
129 {
130 	int i;
131 	UINTN x, y;
132 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
133 
134 	if (c == '\t')
135 		/* XXX lame tab expansion */
136 		for (i = 0; i < 8; i++)
137 			efi_cons_rawputchar(' ');
138 	else {
139 #ifndef	TERM_EMU
140 		if (c == '\n')
141 			efi_cons_efiputchar('\r');
142 		else
143 			efi_cons_efiputchar(c);
144 #else
145 		switch (c) {
146 		case '\r':
147 			curx = 0;
148 			curs_move(&curx, &cury, curx, cury);
149 			return;
150 		case '\n':
151 			cury++;
152 			if (cury >= y) {
153 				efi_cons_efiputchar('\n');
154 				cury--;
155 			} else
156 				curs_move(&curx, &cury, curx, cury);
157 			return;
158 		case '\b':
159 			if (curx > 0) {
160 				curx--;
161 				curs_move(&curx, &cury, curx, cury);
162 			}
163 			return;
164 		default:
165 			efi_cons_efiputchar(c);
166 			curx++;
167 			if (curx > x-1) {
168 				curx = 0;
169 				cury++;
170 			}
171 			if (cury > y-1) {
172 				curx = 0;
173 				cury--;
174 			}
175 		}
176 		curs_move(&curx, &cury, curx, cury);
177 #endif
178 	}
179 }
180 
181 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
182 static void
183 bail_out(int c)
184 {
185 	char buf[16], *ch;
186 	int i;
187 
188 	if (esc) {
189 		efi_cons_rawputchar('\033');
190 		if (esc != '\033')
191 			efi_cons_rawputchar(esc);
192 		for (i = 0; i <= argc; ++i) {
193 			sprintf(buf, "%d", args[i]);
194 			ch = buf;
195 			while (*ch)
196 				efi_cons_rawputchar(*ch++);
197 		}
198 	}
199 	efi_cons_rawputchar(c);
200 	end_term();
201 }
202 
203 /* Clear display from current position to end of screen. */
204 static void
205 CD(void) {
206 	int i;
207 	UINTN x, y;
208 
209 	get_pos(&curx, &cury);
210 	if (curx == 0 && cury == 0) {
211 		conout->ClearScreen(conout);
212 		end_term();
213 		return;
214 	}
215 
216 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
217 	CL(0);  /* clear current line from cursor to end */
218 	for (i = cury + 1; i < y-1; i++) {
219 		curs_move(NULL, NULL, 0, i);
220 		CL(0);
221 	}
222 	curs_move(NULL, NULL, curx, cury);
223 	end_term();
224 }
225 
226 /*
227  * Absolute cursor move to args[0] rows and args[1] columns
228  * (the coordinates are 1-based).
229  */
230 static void
231 CM(void)
232 {
233 	if (args[0] > 0)
234 		args[0]--;
235 	if (args[1] > 0)
236 		args[1]--;
237 	curs_move(&curx, &cury, args[1], args[0]);
238 	end_term();
239 }
240 
241 /* Home cursor (left top corner), also called from mode command. */
242 void
243 HO(void)
244 {
245 	argc = 1;
246 	args[0] = args[1] = 1;
247 	CM();
248 }
249 
250 /* Clear line from current position to end of line */
251 static void
252 CL(int direction)
253 {
254 	int i, len;
255 	UINTN x, y;
256 	CHAR16 *line;
257 
258 	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
259 	switch (direction) {
260 	case 0:         /* from cursor to end */
261 		len = x - curx + 1;
262 		break;
263 	case 1:         /* from beginning to cursor */
264 		len = curx;
265 		break;
266 	case 2:         /* entire line */
267 		len = x;
268 		break;
269 	}
270 
271 	if (cury == y - 1)
272 		len--;
273 
274 	line = malloc(len * sizeof (CHAR16));
275 	if (line == NULL) {
276 		printf("out of memory\n");
277 		return;
278 	}
279 	for (i = 0; i < len; i++)
280 		line[i] = ' ';
281 	line[len-1] = 0;
282 
283 	if (direction != 0)
284 		curs_move(NULL, NULL, 0, cury);
285 
286 	conout->OutputString(conout, line);
287 	/* restore cursor position */
288 	curs_move(NULL, NULL, curx, cury);
289 	free(line);
290 	end_term();
291 }
292 
293 static void
294 get_arg(int c)
295 {
296 	if (argc < 0)
297 		argc = 0;
298 	args[argc] *= 10;
299 	args[argc] += c - '0';
300 }
301 
302 /* Emulate basic capabilities of cons25 terminal */
303 static void
304 efi_term_emu(int c)
305 {
306 	static int ansi_col[] = {
307 		0, 4, 2, 6, 1, 5, 3, 7
308 	};
309 	int t, i;
310 
311 	switch (esc) {
312 	case 0:
313 		switch (c) {
314 		case '\033':
315 			esc = c;
316 			break;
317 		default:
318 			efi_cons_rawputchar(c);
319 			break;
320 		}
321 		break;
322 	case '\033':
323 		switch (c) {
324 		case '[':
325 			esc = c;
326 			args[0] = 0;
327 			argc = -1;
328 			break;
329 		default:
330 			bail_out(c);
331 			break;
332 		}
333 		break;
334 	case '[':
335 		switch (c) {
336 		case ';':
337 			if (argc < 0)
338 				argc = 0;
339 			else if (argc + 1 >= MAXARGS)
340 				bail_out(c);
341 			else
342 				args[++argc] = 0;
343 			break;
344 		case 'H':               /* ho = \E[H */
345 			if (argc < 0)
346 				HO();
347 			else if (argc == 1)
348 				CM();
349 			else
350 				bail_out(c);
351 			break;
352 		case 'J':               /* cd = \E[J */
353 			if (argc < 0)
354 				CD();
355 			else
356 				bail_out(c);
357 			break;
358 		case 'm':
359 			if (argc < 0) {
360 				fg_c = DEFAULT_FGCOLOR;
361 				bg_c = DEFAULT_BGCOLOR;
362 			}
363 			for (i = 0; i <= argc; ++i) {
364 				switch (args[i]) {
365 				case 0:         /* back to normal */
366 					fg_c = DEFAULT_FGCOLOR;
367 					bg_c = DEFAULT_BGCOLOR;
368 					break;
369 				case 1:         /* bold */
370 					fg_c |= 0x8;
371 					break;
372 				case 4:         /* underline */
373 				case 5:         /* blink */
374 					bg_c |= 0x8;
375 					break;
376 				case 7:         /* reverse */
377 					t = fg_c;
378 					fg_c = bg_c;
379 					bg_c = t;
380 					break;
381 				case 30: case 31: case 32: case 33:
382 				case 34: case 35: case 36: case 37:
383 					fg_c = ansi_col[args[i] - 30];
384 					break;
385 				case 39:        /* normal */
386 					fg_c = DEFAULT_FGCOLOR;
387 					break;
388 				case 40: case 41: case 42: case 43:
389 				case 44: case 45: case 46: case 47:
390 					bg_c = ansi_col[args[i] - 40];
391 					break;
392 				case 49:        /* normal */
393 					bg_c = DEFAULT_BGCOLOR;
394 					break;
395 				}
396 			}
397 			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
398 			end_term();
399 			break;
400 		default:
401 			if (isdigit(c))
402 				get_arg(c);
403 			else
404 				bail_out(c);
405 			break;
406 		}
407 		break;
408 	default:
409 		bail_out(c);
410 		break;
411 	}
412 }
413 
414 void
415 efi_cons_putchar(int c)
416 {
417 #ifdef TERM_EMU
418 	efi_term_emu(c);
419 #else
420 	efi_cons_rawputchar(c);
421 #endif
422 }
423 
424 int
425 efi_cons_getchar(void)
426 {
427 	EFI_INPUT_KEY key;
428 	EFI_STATUS status;
429 	UINTN junk;
430 
431 again:
432 	/* Try to read a key stroke. We wait for one if none is pending. */
433 	status = conin->ReadKeyStroke(conin, &key);
434 	if (status == EFI_NOT_READY) {
435 		BS->WaitForEvent(1, &conin->WaitForKey, &junk);
436 		goto again;
437 	}
438 	switch (key.ScanCode) {
439 	case 0x17: /* ESC */
440 		return (0x1b);  /* esc */
441 	}
442 
443 	/* this can return  */
444 	return (key.UnicodeChar);
445 }
446 
447 int
448 efi_cons_poll(void)
449 {
450 	/* This can clear the signaled state. */
451 	return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
452 }
453 
454 /* Plain direct access to EFI OutputString(). */
455 void
456 efi_cons_efiputchar(int c)
457 {
458 	CHAR16 buf[2];
459 
460 	/*
461 	 * translate box chars to unicode
462 	 */
463 	switch (c) {
464 	/* single frame */
465 	case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
466 	case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
467 	case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
468 	case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
469 	case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
470 	case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
471 
472 	/* double frame */
473 	case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
474 	case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
475 	case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
476 	case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
477 	case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
478 	case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
479 
480 	default:
481 		buf[0] = c;
482 	}
483         buf[1] = 0;     /* terminate string */
484 
485 	conout->OutputString(conout, buf);
486 }
487