efi_console.c revision 293724
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
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 void efi_cons_probe(struct console *);
55static int efi_cons_init(int);
56void efi_cons_putchar(int);
57int efi_cons_getchar(void);
58void efi_cons_efiputchar(int);
59int efi_cons_poll(void);
60
61struct 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. */
75void
76get_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). */
83void
84curs_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. */
94void
95end_term(void)
96{
97	esc = 0;
98	argc = -1;
99}
100
101#endif
102
103static void
104efi_cons_probe(struct console *cp)
105{
106	conout = ST->ConOut;
107	conin = ST->ConIn;
108	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
109}
110
111static int
112efi_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
127static void
128efi_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. */
182static void
183bail_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. */
204static void
205CD(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 */
230static void
231CM(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. */
242void
243HO(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 */
251static void
252CL(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
293static void
294get_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 */
303static void
304efi_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
414void
415efi_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
424int
425efi_cons_getchar()
426{
427	EFI_INPUT_KEY key;
428	EFI_STATUS status;
429	UINTN junk;
430
431	/* Try to read a key stroke. We wait for one if none is pending. */
432	status = conin->ReadKeyStroke(conin, &key);
433	if (status == EFI_NOT_READY) {
434		BS->WaitForEvent(1, &conin->WaitForKey, &junk);
435		status = conin->ReadKeyStroke(conin, &key);
436	}
437	switch (key.ScanCode) {
438	case 0x17: /* ESC */
439		return (0x1b);  /* esc */
440	}
441
442	/* this can return  */
443	return (key.UnicodeChar);
444}
445
446int
447efi_cons_poll()
448{
449	/* This can clear the signaled state. */
450	return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
451}
452
453/* Plain direct access to EFI OutputString(). */
454void
455efi_cons_efiputchar(int c)
456{
457	CHAR16 buf[2];
458
459	/*
460	 * translate box chars to unicode
461	 */
462	switch (c) {
463	/* single frame */
464	case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
465	case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
466	case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
467	case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
468	case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
469	case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
470
471	/* double frame */
472	case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
473	case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
474	case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
475	case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
476	case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
477	case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
478
479	default:
480		buf[0] = c;
481	}
482        buf[1] = 0;     /* terminate string */
483
484	conout->OutputString(conout, buf);
485}
486