xref: /openbmc/u-boot/lib/efi_loader/efi_console.c (revision ba7bd5c2baf4a375e1c3f23fe9ede503f6435147)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  EFI application console interface
4  *
5  *  Copyright (c) 2016 Alexander Graf
6  */
7 
8 #include <common.h>
9 #include <charset.h>
10 #include <dm/device.h>
11 #include <efi_loader.h>
12 #include <stdio_dev.h>
13 #include <video_console.h>
14 
15 #define EFI_COUT_MODE_2 2
16 #define EFI_MAX_COUT_MODE 3
17 
18 struct cout_mode {
19 	unsigned long columns;
20 	unsigned long rows;
21 	int present;
22 };
23 
24 static struct cout_mode efi_cout_modes[] = {
25 	/* EFI Mode 0 is 80x25 and always present */
26 	{
27 		.columns = 80,
28 		.rows = 25,
29 		.present = 1,
30 	},
31 	/* EFI Mode 1 is always 80x50 */
32 	{
33 		.columns = 80,
34 		.rows = 50,
35 		.present = 0,
36 	},
37 	/* Value are unknown until we query the console */
38 	{
39 		.columns = 0,
40 		.rows = 0,
41 		.present = 0,
42 	},
43 };
44 
45 const efi_guid_t efi_guid_text_output_protocol =
46 			EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID;
47 const efi_guid_t efi_guid_text_input_protocol =
48 			EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
49 
50 #define cESC '\x1b'
51 #define ESC "\x1b"
52 
53 /* Default to mode 0 */
54 static struct simple_text_output_mode efi_con_mode = {
55 	.max_mode = 1,
56 	.mode = 0,
57 	.attribute = 0,
58 	.cursor_column = 0,
59 	.cursor_row = 0,
60 	.cursor_visible = 1,
61 };
62 
63 /*
64  * Receive and parse a reply from the terminal.
65  *
66  * @n:		array of return values
67  * @num:	number of return values expected
68  * @end_char:	character indicating end of terminal message
69  * @return:	non-zero indicates error
70  */
71 static int term_read_reply(int *n, int num, char end_char)
72 {
73 	char c;
74 	int i = 0;
75 
76 	c = getc();
77 	if (c != cESC)
78 		return -1;
79 	c = getc();
80 	if (c != '[')
81 		return -1;
82 
83 	n[0] = 0;
84 	while (1) {
85 		c = getc();
86 		if (c == ';') {
87 			i++;
88 			if (i >= num)
89 				return -1;
90 			n[i] = 0;
91 			continue;
92 		} else if (c == end_char) {
93 			break;
94 		} else if (c > '9' || c < '0') {
95 			return -1;
96 		}
97 
98 		/* Read one more decimal position */
99 		n[i] *= 10;
100 		n[i] += c - '0';
101 	}
102 	if (i != num - 1)
103 		return -1;
104 
105 	return 0;
106 }
107 
108 static efi_status_t EFIAPI efi_cout_output_string(
109 			struct efi_simple_text_output_protocol *this,
110 			const efi_string_t string)
111 {
112 	struct simple_text_output_mode *con = &efi_con_mode;
113 	struct cout_mode *mode = &efi_cout_modes[con->mode];
114 	char *buf, *pos;
115 	u16 *p;
116 	efi_status_t ret = EFI_SUCCESS;
117 
118 	EFI_ENTRY("%p, %p", this, string);
119 
120 	buf = malloc(utf16_utf8_strlen(string) + 1);
121 	if (!buf) {
122 		ret = EFI_OUT_OF_RESOURCES;
123 		goto out;
124 	}
125 	pos = buf;
126 	utf16_utf8_strcpy(&pos, string);
127 	fputs(stdout, buf);
128 	free(buf);
129 
130 	/*
131 	 * Update the cursor position.
132 	 *
133 	 * The UEFI spec provides advance rules for U+0000, U+0008, U+000A,
134 	 * and U000D. All other characters, including control characters
135 	 * U+0007 (bel) and U+0009 (tab), have to increase the column by one.
136 	 */
137 	for (p = string; *p; ++p) {
138 		switch (*p) {
139 		case '\b':	/* U+0008, backspace */
140 			con->cursor_column = max(0, con->cursor_column - 1);
141 			break;
142 		case '\n':	/* U+000A, newline */
143 			con->cursor_column = 0;
144 			con->cursor_row++;
145 			break;
146 		case '\r':	/* U+000D, carriage-return */
147 			con->cursor_column = 0;
148 			break;
149 		case 0xd800 ... 0xdbff:
150 			/*
151 			 * Ignore high surrogates, we do not want to count a
152 			 * Unicode character twice.
153 			 */
154 			break;
155 		default:
156 			con->cursor_column++;
157 			break;
158 		}
159 		if (con->cursor_column >= mode->columns) {
160 			con->cursor_column = 0;
161 			con->cursor_row++;
162 		}
163 		con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1);
164 	}
165 
166 out:
167 	return EFI_EXIT(ret);
168 }
169 
170 static efi_status_t EFIAPI efi_cout_test_string(
171 			struct efi_simple_text_output_protocol *this,
172 			const efi_string_t string)
173 {
174 	EFI_ENTRY("%p, %p", this, string);
175 	return EFI_EXIT(EFI_SUCCESS);
176 }
177 
178 static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
179 {
180 	if (!mode->present)
181 		return false;
182 
183 	return (mode->rows == rows) && (mode->columns == cols);
184 }
185 
186 static int query_console_serial(int *rows, int *cols)
187 {
188 	/* Ask the terminal about its size */
189 	int n[3];
190 	u64 timeout;
191 
192 	/* Empty input buffer */
193 	while (tstc())
194 		getc();
195 
196 	printf(ESC"[18t");
197 
198 	/* Check if we have a terminal that understands */
199 	timeout = timer_get_us() + 1000000;
200 	while (!tstc())
201 		if (timer_get_us() > timeout)
202 			return -1;
203 
204 	/* Read {depth,rows,cols} */
205 	if (term_read_reply(n, 3, 't'))
206 		return -1;
207 
208 	*cols = n[2];
209 	*rows = n[1];
210 
211 	return 0;
212 }
213 
214 /*
215  * Update the mode table.
216  *
217  * By default the only mode available is 80x25. If the console has at least 50
218  * lines, enable mode 80x50. If we can query the console size and it is neither
219  * 80x25 nor 80x50, set it as an additional mode.
220  */
221 static void query_console_size(void)
222 {
223 	const char *stdout_name = env_get("stdout");
224 	int rows = 25, cols = 80;
225 
226 	if (stdout_name && !strcmp(stdout_name, "vidconsole") &&
227 	    IS_ENABLED(CONFIG_DM_VIDEO)) {
228 		struct stdio_dev *stdout_dev =
229 			stdio_get_by_name("vidconsole");
230 		struct udevice *dev = stdout_dev->priv;
231 		struct vidconsole_priv *priv =
232 			dev_get_uclass_priv(dev);
233 		rows = priv->rows;
234 		cols = priv->cols;
235 	} else if (query_console_serial(&rows, &cols)) {
236 		return;
237 	}
238 
239 	/* Test if we can have Mode 1 */
240 	if (cols >= 80 && rows >= 50) {
241 		efi_cout_modes[1].present = 1;
242 		efi_con_mode.max_mode = 2;
243 	}
244 
245 	/*
246 	 * Install our mode as mode 2 if it is different
247 	 * than mode 0 or 1 and set it as the currently selected mode
248 	 */
249 	if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) &&
250 	    !cout_mode_matches(&efi_cout_modes[1], rows, cols)) {
251 		efi_cout_modes[EFI_COUT_MODE_2].columns = cols;
252 		efi_cout_modes[EFI_COUT_MODE_2].rows = rows;
253 		efi_cout_modes[EFI_COUT_MODE_2].present = 1;
254 		efi_con_mode.max_mode = EFI_MAX_COUT_MODE;
255 		efi_con_mode.mode = EFI_COUT_MODE_2;
256 	}
257 }
258 
259 static efi_status_t EFIAPI efi_cout_query_mode(
260 			struct efi_simple_text_output_protocol *this,
261 			unsigned long mode_number, unsigned long *columns,
262 			unsigned long *rows)
263 {
264 	EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
265 
266 	if (mode_number >= efi_con_mode.max_mode)
267 		return EFI_EXIT(EFI_UNSUPPORTED);
268 
269 	if (efi_cout_modes[mode_number].present != 1)
270 		return EFI_EXIT(EFI_UNSUPPORTED);
271 
272 	if (columns)
273 		*columns = efi_cout_modes[mode_number].columns;
274 	if (rows)
275 		*rows = efi_cout_modes[mode_number].rows;
276 
277 	return EFI_EXIT(EFI_SUCCESS);
278 }
279 
280 static efi_status_t EFIAPI efi_cout_set_mode(
281 			struct efi_simple_text_output_protocol *this,
282 			unsigned long mode_number)
283 {
284 	EFI_ENTRY("%p, %ld", this, mode_number);
285 
286 
287 	if (mode_number > efi_con_mode.max_mode)
288 		return EFI_EXIT(EFI_UNSUPPORTED);
289 
290 	efi_con_mode.mode = mode_number;
291 	efi_con_mode.cursor_column = 0;
292 	efi_con_mode.cursor_row = 0;
293 
294 	return EFI_EXIT(EFI_SUCCESS);
295 }
296 
297 static const struct {
298 	unsigned int fg;
299 	unsigned int bg;
300 } color[] = {
301 	{ 30, 40 },     /* 0: black */
302 	{ 34, 44 },     /* 1: blue */
303 	{ 32, 42 },     /* 2: green */
304 	{ 36, 46 },     /* 3: cyan */
305 	{ 31, 41 },     /* 4: red */
306 	{ 35, 45 },     /* 5: magenta */
307 	{ 33, 43 },     /* 6: brown, map to yellow as edk2 does*/
308 	{ 37, 47 },     /* 7: light grey, map to white */
309 };
310 
311 /* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */
312 static efi_status_t EFIAPI efi_cout_set_attribute(
313 			struct efi_simple_text_output_protocol *this,
314 			unsigned long attribute)
315 {
316 	unsigned int bold = EFI_ATTR_BOLD(attribute);
317 	unsigned int fg = EFI_ATTR_FG(attribute);
318 	unsigned int bg = EFI_ATTR_BG(attribute);
319 
320 	EFI_ENTRY("%p, %lx", this, attribute);
321 
322 	if (attribute)
323 		printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg);
324 	else
325 		printf(ESC"[0;37;40m");
326 
327 	return EFI_EXIT(EFI_SUCCESS);
328 }
329 
330 static efi_status_t EFIAPI efi_cout_clear_screen(
331 			struct efi_simple_text_output_protocol *this)
332 {
333 	EFI_ENTRY("%p", this);
334 
335 	printf(ESC"[2J");
336 	efi_con_mode.cursor_column = 0;
337 	efi_con_mode.cursor_row = 0;
338 
339 	return EFI_EXIT(EFI_SUCCESS);
340 }
341 
342 static efi_status_t EFIAPI efi_cout_reset(
343 			struct efi_simple_text_output_protocol *this,
344 			char extended_verification)
345 {
346 	EFI_ENTRY("%p, %d", this, extended_verification);
347 
348 	/* Clear screen */
349 	EFI_CALL(efi_cout_clear_screen(this));
350 	/* Set default colors */
351 	printf(ESC "[0;37;40m");
352 
353 	return EFI_EXIT(EFI_SUCCESS);
354 }
355 
356 static efi_status_t EFIAPI efi_cout_set_cursor_position(
357 			struct efi_simple_text_output_protocol *this,
358 			unsigned long column, unsigned long row)
359 {
360 	EFI_ENTRY("%p, %ld, %ld", this, column, row);
361 
362 	printf(ESC"[%d;%df", (int)row, (int)column);
363 	efi_con_mode.cursor_column = column;
364 	efi_con_mode.cursor_row = row;
365 
366 	return EFI_EXIT(EFI_SUCCESS);
367 }
368 
369 static efi_status_t EFIAPI efi_cout_enable_cursor(
370 			struct efi_simple_text_output_protocol *this,
371 			bool enable)
372 {
373 	EFI_ENTRY("%p, %d", this, enable);
374 
375 	printf(ESC"[?25%c", enable ? 'h' : 'l');
376 
377 	return EFI_EXIT(EFI_SUCCESS);
378 }
379 
380 struct efi_simple_text_output_protocol efi_con_out = {
381 	.reset = efi_cout_reset,
382 	.output_string = efi_cout_output_string,
383 	.test_string = efi_cout_test_string,
384 	.query_mode = efi_cout_query_mode,
385 	.set_mode = efi_cout_set_mode,
386 	.set_attribute = efi_cout_set_attribute,
387 	.clear_screen = efi_cout_clear_screen,
388 	.set_cursor_position = efi_cout_set_cursor_position,
389 	.enable_cursor = efi_cout_enable_cursor,
390 	.mode = (void*)&efi_con_mode,
391 };
392 
393 static efi_status_t EFIAPI efi_cin_reset(
394 			struct efi_simple_input_interface *this,
395 			bool extended_verification)
396 {
397 	EFI_ENTRY("%p, %d", this, extended_verification);
398 
399 	/* Empty input buffer */
400 	while (tstc())
401 		getc();
402 
403 	return EFI_EXIT(EFI_SUCCESS);
404 }
405 
406 /*
407  * Analyze modifiers (shift, alt, ctrl) for function keys.
408  * This gets called when we have already parsed CSI.
409  *
410  * @modifiers:  bitmask (shift, alt, ctrl)
411  * @return:	the unmodified code
412  */
413 static char skip_modifiers(int *modifiers)
414 {
415 	char c, mod = 0, ret = 0;
416 
417 	c = getc();
418 
419 	if (c != ';') {
420 		ret = c;
421 		if (c == '~')
422 			goto out;
423 		c = getc();
424 	}
425 	for (;;) {
426 		switch (c) {
427 		case '0'...'9':
428 			mod *= 10;
429 			mod += c - '0';
430 		/* fall through */
431 		case ';':
432 			c = getc();
433 			break;
434 		default:
435 			goto out;
436 		}
437 	}
438 out:
439 	if (mod)
440 		--mod;
441 	if (modifiers)
442 		*modifiers = mod;
443 	if (!ret)
444 		ret = c;
445 	return ret;
446 }
447 
448 static efi_status_t EFIAPI efi_cin_read_key_stroke(
449 			struct efi_simple_input_interface *this,
450 			struct efi_input_key *key)
451 {
452 	struct efi_input_key pressed_key = {
453 		.scan_code = 0,
454 		.unicode_char = 0,
455 	};
456 	char ch;
457 
458 	EFI_ENTRY("%p, %p", this, key);
459 
460 	/* We don't do interrupts, so check for timers cooperatively */
461 	efi_timer_check();
462 
463 	if (!tstc()) {
464 		/* No key pressed */
465 		return EFI_EXIT(EFI_NOT_READY);
466 	}
467 
468 	ch = getc();
469 	if (ch == cESC) {
470 		/*
471 		 * Xterm Control Sequences
472 		 * https://www.xfree86.org/4.8.0/ctlseqs.html
473 		 */
474 		ch = getc();
475 		switch (ch) {
476 		case cESC: /* ESC */
477 			pressed_key.scan_code = 23;
478 			break;
479 		case 'O': /* F1 - F4 */
480 			ch = getc();
481 			/* skip modifiers */
482 			if (ch <= '9')
483 				ch = getc();
484 			pressed_key.scan_code = ch - 'P' + 11;
485 			break;
486 		case 'a'...'z':
487 			ch = ch - 'a';
488 			break;
489 		case '[':
490 			ch = getc();
491 			switch (ch) {
492 			case 'A'...'D': /* up, down right, left */
493 				pressed_key.scan_code = ch - 'A' + 1;
494 				break;
495 			case 'F': /* End */
496 				pressed_key.scan_code = 6;
497 				break;
498 			case 'H': /* Home */
499 				pressed_key.scan_code = 5;
500 				break;
501 			case '1':
502 				ch = skip_modifiers(NULL);
503 				switch (ch) {
504 				case '1'...'5': /* F1 - F5 */
505 					pressed_key.scan_code = ch - '1' + 11;
506 					break;
507 				case '7'...'9': /* F6 - F8 */
508 					pressed_key.scan_code = ch - '7' + 16;
509 					break;
510 				case 'A'...'D': /* up, down right, left */
511 					pressed_key.scan_code = ch - 'A' + 1;
512 					break;
513 				case 'F':
514 					pressed_key.scan_code = 6; /* End */
515 					break;
516 				case 'H':
517 					pressed_key.scan_code = 5; /* Home */
518 					break;
519 				}
520 				break;
521 			case '2':
522 				ch = skip_modifiers(NULL);
523 				switch (ch) {
524 				case '0'...'1': /* F9 - F10 */
525 					pressed_key.scan_code = ch - '0' + 19;
526 					break;
527 				case '3'...'4': /* F11 - F12 */
528 					pressed_key.scan_code = ch - '3' + 21;
529 					break;
530 				case '~': /* INS */
531 					pressed_key.scan_code = 7;
532 					break;
533 				}
534 				break;
535 			case '3': /* DEL */
536 				pressed_key.scan_code = 8;
537 				skip_modifiers(NULL);
538 				break;
539 			case '5': /* PG UP */
540 				pressed_key.scan_code = 9;
541 				skip_modifiers(NULL);
542 				break;
543 			case '6': /* PG DOWN */
544 				pressed_key.scan_code = 10;
545 				skip_modifiers(NULL);
546 				break;
547 			}
548 			break;
549 		}
550 	} else if (ch == 0x7f) {
551 		/* Backspace */
552 		ch = 0x08;
553 	}
554 	if (!pressed_key.scan_code)
555 		pressed_key.unicode_char = ch;
556 	*key = pressed_key;
557 
558 	return EFI_EXIT(EFI_SUCCESS);
559 }
560 
561 struct efi_simple_input_interface efi_con_in = {
562 	.reset = efi_cin_reset,
563 	.read_key_stroke = efi_cin_read_key_stroke,
564 	.wait_for_key = NULL,
565 };
566 
567 static struct efi_event *console_timer_event;
568 
569 static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
570 {
571 }
572 
573 /*
574  * Notification function of the console timer event.
575  *
576  * event:	console timer event
577  * context:	not used
578  */
579 static void EFIAPI efi_console_timer_notify(struct efi_event *event,
580 					    void *context)
581 {
582 	EFI_ENTRY("%p, %p", event, context);
583 
584 	/* Check if input is available */
585 	if (tstc()) {
586 		/* Queue the wait for key event */
587 		efi_con_in.wait_for_key->is_signaled = true;
588 		efi_signal_event(efi_con_in.wait_for_key, true);
589 	}
590 	EFI_EXIT(EFI_SUCCESS);
591 }
592 
593 /* This gets called from do_bootefi_exec(). */
594 int efi_console_register(void)
595 {
596 	efi_status_t r;
597 	struct efi_object *efi_console_output_obj;
598 	struct efi_object *efi_console_input_obj;
599 
600 	/* Set up mode information */
601 	query_console_size();
602 
603 	/* Create handles */
604 	r = efi_create_handle((efi_handle_t *)&efi_console_output_obj);
605 	if (r != EFI_SUCCESS)
606 		goto out_of_memory;
607 	r = efi_add_protocol(efi_console_output_obj->handle,
608 			     &efi_guid_text_output_protocol, &efi_con_out);
609 	if (r != EFI_SUCCESS)
610 		goto out_of_memory;
611 	r = efi_create_handle((efi_handle_t *)&efi_console_input_obj);
612 	if (r != EFI_SUCCESS)
613 		goto out_of_memory;
614 	r = efi_add_protocol(efi_console_input_obj->handle,
615 			     &efi_guid_text_input_protocol, &efi_con_in);
616 	if (r != EFI_SUCCESS)
617 		goto out_of_memory;
618 
619 	/* Create console events */
620 	r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
621 			     NULL, NULL, &efi_con_in.wait_for_key);
622 	if (r != EFI_SUCCESS) {
623 		printf("ERROR: Failed to register WaitForKey event\n");
624 		return r;
625 	}
626 	r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
627 			     efi_console_timer_notify, NULL, NULL,
628 			     &console_timer_event);
629 	if (r != EFI_SUCCESS) {
630 		printf("ERROR: Failed to register console event\n");
631 		return r;
632 	}
633 	/* 5000 ns cycle is sufficient for 2 MBaud */
634 	r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50);
635 	if (r != EFI_SUCCESS)
636 		printf("ERROR: Failed to set console timer\n");
637 	return r;
638 out_of_memory:
639 	printf("ERROR: Out of meemory\n");
640 	return r;
641 }
642