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