efi_console.c revision 344411
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/11/stand/efi/libefi/efi_console.c 344411 2019-02-21 02:50:45Z kevans $");
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);
50void HO(void);
51void end_term(void);
52#endif
53
54static EFI_INPUT_KEY key_cur;
55static int key_pending;
56
57static void efi_cons_probe(struct console *);
58static int efi_cons_init(int);
59void efi_cons_putchar(int);
60int efi_cons_getchar(void);
61void efi_cons_efiputchar(int);
62int efi_cons_poll(void);
63
64struct 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. */
78void
79get_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). */
86void
87curs_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. */
97void
98end_term(void)
99{
100	esc = 0;
101	argc = -1;
102}
103
104#endif
105
106static void
107efi_cons_probe(struct console *cp)
108{
109	conout = ST->ConOut;
110	conin = ST->ConIn;
111	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
112}
113
114static int
115efi_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
130static void
131efi_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			efi_cons_efiputchar('\r');
151			return;
152		case '\n':
153			efi_cons_efiputchar('\n');
154			efi_cons_efiputchar('\r');
155			cury++;
156			if (cury >= y)
157				cury--;
158			curx = 0;
159			return;
160		case '\b':
161			if (curx > 0) {
162				efi_cons_efiputchar('\b');
163				curx--;
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#endif
179	}
180}
181
182#ifdef TERM_EMU
183/* Gracefully exit ESC-sequence processing in case of misunderstanding. */
184static void
185bail_out(int c)
186{
187	char buf[16], *ch;
188	int i;
189
190	if (esc) {
191		efi_cons_rawputchar('\033');
192		if (esc != '\033')
193			efi_cons_rawputchar(esc);
194		for (i = 0; i <= argc; ++i) {
195			sprintf(buf, "%d", args[i]);
196			ch = buf;
197			while (*ch)
198				efi_cons_rawputchar(*ch++);
199		}
200	}
201	efi_cons_rawputchar(c);
202	end_term();
203}
204
205/* Clear display from current position to end of screen. */
206static void
207CD(void) {
208	int i;
209	UINTN x, y;
210
211	get_pos(&curx, &cury);
212	if (curx == 0 && cury == 0) {
213		conout->ClearScreen(conout);
214		end_term();
215		return;
216	}
217
218	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
219	CL(0);  /* clear current line from cursor to end */
220	for (i = cury + 1; i < y-1; i++) {
221		curs_move(NULL, NULL, 0, i);
222		CL(0);
223	}
224	curs_move(NULL, NULL, curx, cury);
225	end_term();
226}
227
228/*
229 * Absolute cursor move to args[0] rows and args[1] columns
230 * (the coordinates are 1-based).
231 */
232static void
233CM(void)
234{
235	if (args[0] > 0)
236		args[0]--;
237	if (args[1] > 0)
238		args[1]--;
239	curs_move(&curx, &cury, args[1], args[0]);
240	end_term();
241}
242
243/* Home cursor (left top corner), also called from mode command. */
244void
245HO(void)
246{
247	argc = 1;
248	args[0] = args[1] = 1;
249	CM();
250}
251
252/* Clear line from current position to end of line */
253static void
254CL(int direction)
255{
256	int i, len;
257	UINTN x, y;
258	CHAR16 *line;
259
260	conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
261	switch (direction) {
262	case 0:         /* from cursor to end */
263		len = x - curx + 1;
264		break;
265	case 1:         /* from beginning to cursor */
266		len = curx;
267		break;
268	case 2:         /* entire line */
269		len = x;
270		break;
271	default:	/* NOTREACHED */
272		__unreachable();
273	}
274
275	if (cury == y - 1)
276		len--;
277
278	line = malloc(len * sizeof (CHAR16));
279	if (line == NULL) {
280		printf("out of memory\n");
281		return;
282	}
283	for (i = 0; i < len; i++)
284		line[i] = ' ';
285	line[len-1] = 0;
286
287	if (direction != 0)
288		curs_move(NULL, NULL, 0, cury);
289
290	conout->OutputString(conout, line);
291	/* restore cursor position */
292	curs_move(NULL, NULL, curx, cury);
293	free(line);
294	end_term();
295}
296
297static void
298get_arg(int c)
299{
300	if (argc < 0)
301		argc = 0;
302	args[argc] *= 10;
303	args[argc] += c - '0';
304}
305
306/* Emulate basic capabilities of cons25 terminal */
307static void
308efi_term_emu(int c)
309{
310	static int ansi_col[] = {
311		0, 4, 2, 6, 1, 5, 3, 7
312	};
313	int t, i;
314
315	switch (esc) {
316	case 0:
317		switch (c) {
318		case '\033':
319			esc = c;
320			break;
321		default:
322			efi_cons_rawputchar(c);
323			break;
324		}
325		break;
326	case '\033':
327		switch (c) {
328		case '[':
329			esc = c;
330			args[0] = 0;
331			argc = -1;
332			break;
333		default:
334			bail_out(c);
335			break;
336		}
337		break;
338	case '[':
339		switch (c) {
340		case ';':
341			if (argc < 0)
342				argc = 0;
343			else if (argc + 1 >= MAXARGS)
344				bail_out(c);
345			else
346				args[++argc] = 0;
347			break;
348		case 'H':               /* ho = \E[H */
349			if (argc < 0)
350				HO();
351			else if (argc == 1)
352				CM();
353			else
354				bail_out(c);
355			break;
356		case 'J':               /* cd = \E[J */
357			if (argc < 0)
358				CD();
359			else
360				bail_out(c);
361			break;
362		case 'm':
363			if (argc < 0) {
364				fg_c = DEFAULT_FGCOLOR;
365				bg_c = DEFAULT_BGCOLOR;
366			}
367			for (i = 0; i <= argc; ++i) {
368				switch (args[i]) {
369				case 0:         /* back to normal */
370					fg_c = DEFAULT_FGCOLOR;
371					bg_c = DEFAULT_BGCOLOR;
372					break;
373				case 1:         /* bold */
374					fg_c |= 0x8;
375					break;
376				case 4:         /* underline */
377				case 5:         /* blink */
378					bg_c |= 0x8;
379					break;
380				case 7:         /* reverse */
381					t = fg_c;
382					fg_c = bg_c;
383					bg_c = t;
384					break;
385				case 22:	/* normal intensity */
386					fg_c &= ~0x8;
387					break;
388				case 24:	/* not underline */
389				case 25:	/* not blinking */
390					bg_c &= ~0x8;
391					break;
392				case 30: case 31: case 32: case 33:
393				case 34: case 35: case 36: case 37:
394					fg_c = ansi_col[args[i] - 30];
395					break;
396				case 39:        /* normal */
397					fg_c = DEFAULT_FGCOLOR;
398					break;
399				case 40: case 41: case 42: case 43:
400				case 44: case 45: case 46: case 47:
401					bg_c = ansi_col[args[i] - 40];
402					break;
403				case 49:        /* normal */
404					bg_c = DEFAULT_BGCOLOR;
405					break;
406				}
407			}
408			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
409			end_term();
410			break;
411		default:
412			if (isdigit(c))
413				get_arg(c);
414			else
415				bail_out(c);
416			break;
417		}
418		break;
419	default:
420		bail_out(c);
421		break;
422	}
423}
424#else
425void
426HO(void)
427{
428}
429#endif
430
431void
432efi_cons_putchar(int c)
433{
434#ifdef TERM_EMU
435	efi_term_emu(c);
436#else
437	efi_cons_rawputchar(c);
438#endif
439}
440
441int
442efi_cons_getchar()
443{
444	EFI_INPUT_KEY key;
445	EFI_STATUS status;
446	UINTN junk;
447
448	if (key_pending) {
449		key = key_cur;
450		key_pending = 0;
451	} else {
452		/* Try to read a key stroke. We wait for one if none is pending. */
453		status = conin->ReadKeyStroke(conin, &key);
454		while (status == EFI_NOT_READY) {
455			/* Some EFI implementation (u-boot for example) do not support WaitForKey */
456			if (conin->WaitForKey != NULL)
457				BS->WaitForEvent(1, &conin->WaitForKey, &junk);
458			status = conin->ReadKeyStroke(conin, &key);
459		}
460	}
461
462	switch (key.ScanCode) {
463	case 0x17: /* ESC */
464		return (0x1b);  /* esc */
465	}
466
467	/* this can return  */
468	return (key.UnicodeChar);
469}
470
471int
472efi_cons_poll()
473{
474	EFI_INPUT_KEY key;
475	EFI_STATUS status;
476
477	if (conin->WaitForKey == NULL) {
478		if (key_pending)
479			return (1);
480		status = conin->ReadKeyStroke(conin, &key);
481		if (status == EFI_SUCCESS) {
482			key_cur = key;
483			key_pending = 1;
484		}
485		return (key_pending);
486	}
487
488	/* This can clear the signaled state. */
489	return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
490}
491
492/* Plain direct access to EFI OutputString(). */
493void
494efi_cons_efiputchar(int c)
495{
496	CHAR16 buf[2];
497
498	/*
499	 * translate box chars to unicode
500	 */
501	switch (c) {
502	/* single frame */
503	case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
504	case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
505	case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
506	case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
507	case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
508	case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
509
510	/* double frame */
511	case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
512	case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
513	case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
514	case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
515	case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
516	case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
517
518	default:
519		buf[0] = c;
520	}
521        buf[1] = 0;     /* terminate string */
522
523	conout->OutputString(conout, buf);
524}
525