efi_console.c revision 299972
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 299972 2016-05-16 20:00:09Z pfg $");
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	default:	/* NOTREACHED */
270		__unreachable();
271	}
272
273	if (cury == y - 1)
274		len--;
275
276	line = malloc(len * sizeof (CHAR16));
277	if (line == NULL) {
278		printf("out of memory\n");
279		return;
280	}
281	for (i = 0; i < len; i++)
282		line[i] = ' ';
283	line[len-1] = 0;
284
285	if (direction != 0)
286		curs_move(NULL, NULL, 0, cury);
287
288	conout->OutputString(conout, line);
289	/* restore cursor position */
290	curs_move(NULL, NULL, curx, cury);
291	free(line);
292	end_term();
293}
294
295static void
296get_arg(int c)
297{
298	if (argc < 0)
299		argc = 0;
300	args[argc] *= 10;
301	args[argc] += c - '0';
302}
303
304/* Emulate basic capabilities of cons25 terminal */
305static void
306efi_term_emu(int c)
307{
308	static int ansi_col[] = {
309		0, 4, 2, 6, 1, 5, 3, 7
310	};
311	int t, i;
312
313	switch (esc) {
314	case 0:
315		switch (c) {
316		case '\033':
317			esc = c;
318			break;
319		default:
320			efi_cons_rawputchar(c);
321			break;
322		}
323		break;
324	case '\033':
325		switch (c) {
326		case '[':
327			esc = c;
328			args[0] = 0;
329			argc = -1;
330			break;
331		default:
332			bail_out(c);
333			break;
334		}
335		break;
336	case '[':
337		switch (c) {
338		case ';':
339			if (argc < 0)
340				argc = 0;
341			else if (argc + 1 >= MAXARGS)
342				bail_out(c);
343			else
344				args[++argc] = 0;
345			break;
346		case 'H':               /* ho = \E[H */
347			if (argc < 0)
348				HO();
349			else if (argc == 1)
350				CM();
351			else
352				bail_out(c);
353			break;
354		case 'J':               /* cd = \E[J */
355			if (argc < 0)
356				CD();
357			else
358				bail_out(c);
359			break;
360		case 'm':
361			if (argc < 0) {
362				fg_c = DEFAULT_FGCOLOR;
363				bg_c = DEFAULT_BGCOLOR;
364			}
365			for (i = 0; i <= argc; ++i) {
366				switch (args[i]) {
367				case 0:         /* back to normal */
368					fg_c = DEFAULT_FGCOLOR;
369					bg_c = DEFAULT_BGCOLOR;
370					break;
371				case 1:         /* bold */
372					fg_c |= 0x8;
373					break;
374				case 4:         /* underline */
375				case 5:         /* blink */
376					bg_c |= 0x8;
377					break;
378				case 7:         /* reverse */
379					t = fg_c;
380					fg_c = bg_c;
381					bg_c = t;
382					break;
383				case 30: case 31: case 32: case 33:
384				case 34: case 35: case 36: case 37:
385					fg_c = ansi_col[args[i] - 30];
386					break;
387				case 39:        /* normal */
388					fg_c = DEFAULT_FGCOLOR;
389					break;
390				case 40: case 41: case 42: case 43:
391				case 44: case 45: case 46: case 47:
392					bg_c = ansi_col[args[i] - 40];
393					break;
394				case 49:        /* normal */
395					bg_c = DEFAULT_BGCOLOR;
396					break;
397				}
398			}
399			conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
400			end_term();
401			break;
402		default:
403			if (isdigit(c))
404				get_arg(c);
405			else
406				bail_out(c);
407			break;
408		}
409		break;
410	default:
411		bail_out(c);
412		break;
413	}
414}
415
416void
417efi_cons_putchar(int c)
418{
419#ifdef TERM_EMU
420	efi_term_emu(c);
421#else
422	efi_cons_rawputchar(c);
423#endif
424}
425
426int
427efi_cons_getchar()
428{
429	EFI_INPUT_KEY key;
430	EFI_STATUS status;
431	UINTN junk;
432
433	/* Try to read a key stroke. We wait for one if none is pending. */
434	status = conin->ReadKeyStroke(conin, &key);
435	if (status == EFI_NOT_READY) {
436		BS->WaitForEvent(1, &conin->WaitForKey, &junk);
437		status = conin->ReadKeyStroke(conin, &key);
438	}
439	switch (key.ScanCode) {
440	case 0x17: /* ESC */
441		return (0x1b);  /* esc */
442	}
443
444	/* this can return  */
445	return (key.UnicodeChar);
446}
447
448int
449efi_cons_poll()
450{
451	/* This can clear the signaled state. */
452	return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
453}
454
455/* Plain direct access to EFI OutputString(). */
456void
457efi_cons_efiputchar(int c)
458{
459	CHAR16 buf[2];
460
461	/*
462	 * translate box chars to unicode
463	 */
464	switch (c) {
465	/* single frame */
466	case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
467	case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
468	case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
469	case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
470	case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
471	case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
472
473	/* double frame */
474	case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
475	case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
476	case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
477	case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
478	case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
479	case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
480
481	default:
482		buf[0] = c;
483	}
484        buf[1] = 0;     /* terminate string */
485
486	conout->OutputString(conout, buf);
487}
488