xref: /freebsd/stand/efi/libefi/efi_console.c (revision 2c3f47a727372086b41ef9ce06eb1f1eb83a67d3)
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$");
29 
30 #include <efi.h>
31 #include <efilib.h>
32 #include <teken.h>
33 
34 #include "bootstrap.h"
35 
36 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
37 static SIMPLE_TEXT_OUTPUT_INTERFACE	*conout;
38 static SIMPLE_INPUT_INTERFACE		*conin;
39 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
40 
41 static tf_bell_t	efi_cons_bell;
42 static tf_cursor_t	efi_text_cursor;
43 static tf_putchar_t	efi_text_putchar;
44 static tf_fill_t	efi_text_fill;
45 static tf_copy_t	efi_text_copy;
46 static tf_param_t	efi_text_param;
47 static tf_respond_t	efi_cons_respond;
48 
49 static teken_funcs_t tf = {
50 	.tf_bell	= efi_cons_bell,
51 	.tf_cursor	= efi_text_cursor,
52 	.tf_putchar	= efi_text_putchar,
53 	.tf_fill	= efi_text_fill,
54 	.tf_copy	= efi_text_copy,
55 	.tf_param	= efi_text_param,
56 	.tf_respond	= efi_cons_respond,
57 };
58 
59 teken_t teken;
60 teken_pos_t tp;
61 
62 struct text_pixel {
63 	teken_char_t c;
64 	teken_attr_t a;
65 };
66 
67 static struct text_pixel *buffer;
68 
69 #define	KEYBUFSZ 10
70 static unsigned keybuf[KEYBUFSZ];	/* keybuf for extended codes */
71 static int key_pending;
72 
73 static const unsigned char teken_color_to_efi_color[16] = {
74 	EFI_BLACK,
75 	EFI_RED,
76 	EFI_GREEN,
77 	EFI_BROWN,
78 	EFI_BLUE,
79 	EFI_MAGENTA,
80 	EFI_CYAN,
81 	EFI_LIGHTGRAY,
82 	EFI_DARKGRAY,
83 	EFI_LIGHTRED,
84 	EFI_LIGHTGREEN,
85 	EFI_YELLOW,
86 	EFI_LIGHTBLUE,
87 	EFI_LIGHTMAGENTA,
88 	EFI_LIGHTCYAN,
89 	EFI_WHITE
90 };
91 
92 static void efi_cons_probe(struct console *);
93 static int efi_cons_init(int);
94 void efi_cons_putchar(int);
95 int efi_cons_getchar(void);
96 void efi_cons_efiputchar(int);
97 int efi_cons_poll(void);
98 
99 struct console efi_console = {
100 	"efi",
101 	"EFI console",
102 	C_WIDEOUT,
103 	efi_cons_probe,
104 	efi_cons_init,
105 	efi_cons_putchar,
106 	efi_cons_getchar,
107 	efi_cons_poll
108 };
109 
110 /*
111  * Not implemented.
112  */
113 static void
114 efi_cons_bell(void *s __unused)
115 {
116 }
117 
118 static void
119 efi_text_cursor(void *s __unused, const teken_pos_t *p)
120 {
121 	UINTN row, col;
122 
123 	(void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
124 
125 	if (p->tp_col == col)
126 		col = p->tp_col - 1;
127 	else
128 		col = p->tp_col;
129 
130 	if (p->tp_row == row)
131 		row = p->tp_row - 1;
132 	else
133 		row = p->tp_row;
134 
135 	conout->SetCursorPosition(conout, col, row);
136 }
137 
138 static void
139 efi_text_printchar(const teken_pos_t *p)
140 {
141 	UINTN a, attr;
142 	struct text_pixel *px;
143 	teken_color_t fg, bg, tmp;
144 
145 	px = buffer + p->tp_col + p->tp_row * tp.tp_col;
146 	a = conout->Mode->Attribute;
147 
148 	fg = teken_256to16(px->a.ta_fgcolor);
149 	bg = teken_256to16(px->a.ta_bgcolor);
150 	if (px->a.ta_format & TF_BOLD)
151 		fg |= TC_LIGHT;
152 	if (px->a.ta_format & TF_BLINK)
153 		bg |= TC_LIGHT;
154 
155 	if (px->a.ta_format & TF_REVERSE) {
156 		tmp = fg;
157 		fg = bg;
158 		bg = tmp;
159 	}
160 
161 	attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg],
162 	    teken_color_to_efi_color[bg] & 0x7);
163 
164 	conout->SetCursorPosition(conout, p->tp_col, p->tp_row);
165 
166 	/* to prvent autoscroll, skip print of lower right char */
167 	if (p->tp_row == tp.tp_row - 1 &&
168 	    p->tp_col == tp.tp_col - 1)
169 		return;
170 
171 	(void) conout->SetAttribute(conout, attr);
172 	efi_cons_efiputchar(px->c);
173 	(void) conout->SetAttribute(conout, a);
174 }
175 
176 static void
177 efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c,
178     const teken_attr_t *a)
179 {
180 	EFI_STATUS status;
181 	int idx;
182 
183 	idx = p->tp_col + p->tp_row * tp.tp_col;
184 	buffer[idx].c = c;
185 	buffer[idx].a = *a;
186 	efi_text_printchar(p);
187 }
188 
189 static void
190 efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c,
191     const teken_attr_t *a)
192 {
193 	teken_pos_t p;
194 	UINTN row, col;
195 
196 	(void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
197 
198 	conout->EnableCursor(conout, FALSE);
199 	for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
200 	    p.tp_row++)
201 		for (p.tp_col = r->tr_begin.tp_col;
202 		    p.tp_col < r->tr_end.tp_col; p.tp_col++)
203 			efi_text_putchar(s, &p, c, a);
204 	conout->EnableCursor(conout, TRUE);
205 }
206 
207 static bool
208 efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
209 {
210 	if (px1->c != px2->c)
211 		return (false);
212 
213 	if (px1->a.ta_format != px2->a.ta_format)
214 		return (false);
215 	if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
216 		return (false);
217 	if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
218 		return (false);
219 
220 	return (true);
221 }
222 
223 static void
224 efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p)
225 {
226 	int srow, drow;
227 	int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */
228 	teken_pos_t d, s;
229 
230 	/*
231 	 * Copying is a little tricky. We must make sure we do it in
232 	 * correct order, to make sure we don't overwrite our own data.
233 	 */
234 
235 	nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
236 	ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
237 
238 	conout->EnableCursor(conout, FALSE);
239 	if (p->tp_row < r->tr_begin.tp_row) {
240 		/* Copy from bottom to top. */
241 		for (y = 0; y < nrow; y++) {
242 			d.tp_row = p->tp_row + y;
243 			s.tp_row = r->tr_begin.tp_row + y;
244 			drow = d.tp_row * tp.tp_col;
245 			srow = s.tp_row * tp.tp_col;
246 			for (x = 0; x < ncol; x++) {
247 				d.tp_col = p->tp_col + x;
248 				s.tp_col = r->tr_begin.tp_col + x;
249 
250 				if (!efi_same_pixel(
251 				    &buffer[d.tp_col + drow],
252 				    &buffer[s.tp_col + srow])) {
253 					buffer[d.tp_col + drow] =
254 					    buffer[s.tp_col + srow];
255 					efi_text_printchar(&d);
256 				}
257 			}
258 		}
259 	} else {
260 		/* Copy from top to bottom. */
261 		if (p->tp_col < r->tr_begin.tp_col) {
262 			/* Copy from right to left. */
263 			for (y = nrow - 1; y >= 0; y--) {
264 				d.tp_row = p->tp_row + y;
265 				s.tp_row = r->tr_begin.tp_row + y;
266 				drow = d.tp_row * tp.tp_col;
267 				srow = s.tp_row * tp.tp_col;
268 				for (x = 0; x < ncol; x++) {
269 					d.tp_col = p->tp_col + x;
270 					s.tp_col = r->tr_begin.tp_col + x;
271 
272 					if (!efi_same_pixel(
273 					    &buffer[d.tp_col + drow],
274 					    &buffer[s.tp_col + srow])) {
275 						buffer[d.tp_col + drow] =
276 						    buffer[s.tp_col + srow];
277 						efi_text_printchar(&d);
278 					}
279 				}
280 			}
281 		} else {
282 			/* Copy from left to right. */
283 			for (y = nrow - 1; y >= 0; y--) {
284 				d.tp_row = p->tp_row + y;
285 				s.tp_row = r->tr_begin.tp_row + y;
286 				drow = d.tp_row * tp.tp_col;
287 				srow = s.tp_row * tp.tp_col;
288 				for (x = ncol - 1; x >= 0; x--) {
289 					d.tp_col = p->tp_col + x;
290 					s.tp_col = r->tr_begin.tp_col + x;
291 
292 					if (!efi_same_pixel(
293 					    &buffer[d.tp_col + drow],
294 					    &buffer[s.tp_col + srow])) {
295 						buffer[d.tp_col + drow] =
296 						    buffer[s.tp_col + srow];
297 						efi_text_printchar(&d);
298 					}
299 				}
300 			}
301 		}
302 	}
303 	conout->EnableCursor(conout, TRUE);
304 }
305 
306 static void
307 efi_text_param(void *s __unused, int cmd, unsigned int value)
308 {
309 	switch (cmd) {
310 	case TP_SETLOCALCURSOR:
311 		/*
312 		 * 0 means normal (usually block), 1 means hidden, and
313 		 * 2 means blinking (always block) for compatibility with
314 		 * syscons.  We don't support any changes except hiding,
315 		 * so must map 2 to 0.
316 		 */
317 		value = (value == 1) ? 0 : 1;
318 		/* FALLTHROUGH */
319 	case TP_SHOWCURSOR:
320 		if (value == 1)
321 			conout->EnableCursor(conout, TRUE);
322 		else
323 			conout->EnableCursor(conout, FALSE);
324 		break;
325 	default:
326 		/* Not yet implemented */
327 		break;
328 	}
329 }
330 
331 /*
332  * Not implemented.
333  */
334 static void
335 efi_cons_respond(void *s __unused, const void *buf __unused,
336     size_t len __unused)
337 {
338 }
339 
340 static void
341 efi_cons_probe(struct console *cp)
342 {
343 	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
344 }
345 
346 bool
347 efi_cons_update_mode(void)
348 {
349 	UINTN cols, rows;
350 	const teken_attr_t *a;
351 	EFI_STATUS status;
352 	char env[8];
353 
354 	status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
355 	if (EFI_ERROR(status)) {
356 		cols = 80;
357 		rows = 24;
358 	}
359 
360 	if (buffer != NULL) {
361 		if (tp.tp_row == rows && tp.tp_col == cols)
362 			return (true);
363 		free(buffer);
364 	} else {
365 		teken_init(&teken, &tf, NULL);
366 	}
367 
368 	tp.tp_row = rows;
369 	tp.tp_col = cols;
370 	buffer = malloc(rows * cols * sizeof(*buffer));
371 	if (buffer == NULL)
372 		return (false);
373 
374 	teken_set_winsize(&teken, &tp);
375 	a = teken_get_defattr(&teken);
376 
377 	for (int row = 0; row < rows; row++)
378 		for (int col = 0; col < cols; col++) {
379 			buffer[col + row * tp.tp_col].c = ' ';
380 			buffer[col + row * tp.tp_col].a = *a;
381 		}
382 
383 	snprintf(env, sizeof (env), "%u", (unsigned)rows);
384 	setenv("LINES", env, 1);
385 	snprintf(env, sizeof (env), "%u", (unsigned)cols);
386 	setenv("COLUMNS", env, 1);
387 
388 	return (true);
389 }
390 
391 static int
392 efi_cons_init(int arg)
393 {
394 	EFI_STATUS status;
395 
396 	if (conin != NULL)
397 		return (0);
398 
399 	conout = ST->ConOut;
400 	conin = ST->ConIn;
401 
402 	conout->EnableCursor(conout, TRUE);
403 	status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
404 	    (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
405 	if (status != EFI_SUCCESS)
406 		coninex = NULL;
407 
408 	if (efi_cons_update_mode())
409 		return (0);
410 
411 	return (1);
412 }
413 
414 void
415 efi_cons_putchar(int c)
416 {
417 	unsigned char ch = c;
418 
419 	if (buffer != NULL)
420 		teken_input(&teken, &ch, sizeof (ch));
421 	else
422 		efi_cons_efiputchar(c);
423 }
424 
425 static int
426 keybuf_getchar(void)
427 {
428 	int i, c = 0;
429 
430 	for (i = 0; i < KEYBUFSZ; i++) {
431 		if (keybuf[i] != 0) {
432 			c = keybuf[i];
433 			keybuf[i] = 0;
434 			break;
435 		}
436 	}
437 
438 	return (c);
439 }
440 
441 static bool
442 keybuf_ischar(void)
443 {
444 	int i;
445 
446 	for (i = 0; i < KEYBUFSZ; i++) {
447 		if (keybuf[i] != 0)
448 			return (true);
449 	}
450 	return (false);
451 }
452 
453 /*
454  * We are not reading input before keybuf is empty, so we are safe
455  * just to fill keybuf from the beginning.
456  */
457 static void
458 keybuf_inschar(EFI_INPUT_KEY *key)
459 {
460 
461 	switch (key->ScanCode) {
462 	case SCAN_UP: /* UP */
463 		keybuf[0] = 0x1b;	/* esc */
464 		keybuf[1] = '[';
465 		keybuf[2] = 'A';
466 		break;
467 	case SCAN_DOWN: /* DOWN */
468 		keybuf[0] = 0x1b;	/* esc */
469 		keybuf[1] = '[';
470 		keybuf[2] = 'B';
471 		break;
472 	case SCAN_RIGHT: /* RIGHT */
473 		keybuf[0] = 0x1b;	/* esc */
474 		keybuf[1] = '[';
475 		keybuf[2] = 'C';
476 		break;
477 	case SCAN_LEFT: /* LEFT */
478 		keybuf[0] = 0x1b;	/* esc */
479 		keybuf[1] = '[';
480 		keybuf[2] = 'D';
481 		break;
482 	case SCAN_DELETE:
483 		keybuf[0] = CHAR_BACKSPACE;
484 		break;
485 	case SCAN_ESC:
486 		keybuf[0] = 0x1b;	/* esc */
487 		break;
488 	default:
489 		keybuf[0] = key->UnicodeChar;
490 		break;
491 	}
492 }
493 
494 static bool
495 efi_readkey(void)
496 {
497 	EFI_STATUS status;
498 	EFI_INPUT_KEY key;
499 
500 	status = conin->ReadKeyStroke(conin, &key);
501 	if (status == EFI_SUCCESS) {
502 		keybuf_inschar(&key);
503 		return (true);
504 	}
505 	return (false);
506 }
507 
508 static bool
509 efi_readkey_ex(void)
510 {
511 	EFI_STATUS status;
512 	EFI_INPUT_KEY *kp;
513 	EFI_KEY_DATA  key_data;
514 	uint32_t kss;
515 
516 	status = coninex->ReadKeyStrokeEx(coninex, &key_data);
517 	if (status == EFI_SUCCESS) {
518 		kss = key_data.KeyState.KeyShiftState;
519 		kp = &key_data.Key;
520 		if (kss & EFI_SHIFT_STATE_VALID) {
521 
522 			/*
523 			 * quick mapping to control chars, replace with
524 			 * map lookup later.
525 			 */
526 			if (kss & EFI_RIGHT_CONTROL_PRESSED ||
527 			    kss & EFI_LEFT_CONTROL_PRESSED) {
528 				if (kp->UnicodeChar >= 'a' &&
529 				    kp->UnicodeChar <= 'z') {
530 					kp->UnicodeChar -= 'a';
531 					kp->UnicodeChar++;
532 				}
533 			}
534 		}
535 
536 		keybuf_inschar(kp);
537 		return (true);
538 	}
539 	return (false);
540 }
541 
542 int
543 efi_cons_getchar(void)
544 {
545 	int c;
546 
547 	if ((c = keybuf_getchar()) != 0)
548 		return (c);
549 
550 	key_pending = 0;
551 
552 	if (coninex == NULL) {
553 		if (efi_readkey())
554 			return (keybuf_getchar());
555 	} else {
556 		if (efi_readkey_ex())
557 			return (keybuf_getchar());
558 	}
559 
560 	return (-1);
561 }
562 
563 int
564 efi_cons_poll(void)
565 {
566 	EFI_STATUS status;
567 
568 	if (keybuf_ischar() || key_pending)
569 		return (1);
570 
571 	/*
572 	 * Some EFI implementation (u-boot for example) do not support
573 	 * WaitForKey().
574 	 * CheckEvent() can clear the signaled state.
575 	 */
576 	if (coninex != NULL) {
577 		if (coninex->WaitForKeyEx == NULL) {
578 			key_pending = efi_readkey_ex();
579 		} else {
580 			status = BS->CheckEvent(coninex->WaitForKeyEx);
581 			key_pending = status == EFI_SUCCESS;
582 		}
583 	} else {
584 		if (conin->WaitForKey == NULL) {
585 			key_pending = efi_readkey();
586 		} else {
587 			status = BS->CheckEvent(conin->WaitForKey);
588 			key_pending = status == EFI_SUCCESS;
589 		}
590 	}
591 
592 	return (key_pending);
593 }
594 
595 /* Plain direct access to EFI OutputString(). */
596 void
597 efi_cons_efiputchar(int c)
598 {
599 	CHAR16 buf[2];
600 	EFI_STATUS status;
601 
602 	buf[0] = c;
603         buf[1] = 0;     /* terminate string */
604 
605 	status = conout->TestString(conout, buf);
606 	if (EFI_ERROR(status))
607 		buf[0] = '?';
608 	conout->OutputString(conout, buf);
609 }
610