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 
29 #include <efi.h>
30 #include <efilib.h>
31 #include <sys/tem_impl.h>
32 #include <sys/multiboot2.h>
33 #include <machine/metadata.h>
34 #include <gfx_fb.h>
35 
36 #include "bootstrap.h"
37 
38 struct efi_fb		efifb;
39 EFI_GRAPHICS_OUTPUT	*gop;
40 EFI_UGA_DRAW_PROTOCOL	*uga;
41 
42 static EFI_GUID ccontrol_protocol_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
43 static EFI_CONSOLE_CONTROL_PROTOCOL	*console_control;
44 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
45 static EFI_CONSOLE_CONTROL_SCREEN_MODE	console_mode;
46 static SIMPLE_TEXT_OUTPUT_INTERFACE	*conout;
47 
48 /* mode change callback and argument from tem */
49 static vis_modechg_cb_t modechg_cb;
50 static struct vis_modechg_arg *modechg_arg;
51 static tem_vt_state_t tem;
52 
53 struct efi_console_data {
54 	struct visual_ops			*ecd_visual_ops;
55 	SIMPLE_INPUT_INTERFACE			*ecd_conin;
56 	EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL	*ecd_coninex;
57 };
58 
59 #define	KEYBUFSZ 10
60 static unsigned keybuf[KEYBUFSZ];	/* keybuf for extended codes */
61 
62 static int key_pending;
63 
64 static const unsigned char solaris_color_to_efi_color[16] = {
65 	EFI_WHITE,
66 	EFI_BLACK,
67 	EFI_BLUE,
68 	EFI_GREEN,
69 	EFI_CYAN,
70 	EFI_RED,
71 	EFI_MAGENTA,
72 	EFI_BROWN,
73 	EFI_LIGHTGRAY,
74 	EFI_DARKGRAY,
75 	EFI_LIGHTBLUE,
76 	EFI_LIGHTGREEN,
77 	EFI_LIGHTCYAN,
78 	EFI_LIGHTRED,
79 	EFI_LIGHTMAGENTA,
80 	EFI_YELLOW
81 };
82 
83 #define	DEFAULT_FGCOLOR	EFI_LIGHTGRAY
84 #define	DEFAULT_BGCOLOR	EFI_BLACK
85 
86 extern int efi_find_framebuffer(struct efi_fb *efifb);
87 
88 static void efi_framebuffer_setup(void);
89 static void efi_cons_probe(struct console *);
90 static int efi_cons_init(struct console *, int);
91 static void efi_cons_putchar(struct console *, int);
92 static void efi_cons_efiputchar(int);
93 static int efi_cons_getchar(struct console *);
94 static int efi_cons_poll(struct console *);
95 static int efi_cons_ioctl(struct console *cp, int cmd, void *data);
96 static void efi_cons_devinfo(struct console *);
97 
98 static int efi_fb_devinit(struct vis_devinit *);
99 static void efi_cons_cursor(struct vis_conscursor *);
100 
101 static int efi_text_devinit(struct vis_devinit *);
102 static int efi_text_cons_clear(struct vis_consclear *);
103 static void efi_text_cons_copy(struct vis_conscopy *);
104 static void efi_text_cons_display(struct vis_consdisplay *);
105 
106 struct console efi_console = {
107 	.c_name = "text",
108 	.c_desc = "EFI console",
109 	.c_flags = C_WIDEOUT,
110 	.c_probe = efi_cons_probe,
111 	.c_init = efi_cons_init,
112 	.c_out = efi_cons_putchar,
113 	.c_in = efi_cons_getchar,
114 	.c_ready = efi_cons_poll,
115 	.c_ioctl = efi_cons_ioctl,
116 	.c_devinfo = efi_cons_devinfo,
117 	.c_private = NULL
118 };
119 
120 static struct vis_identifier fb_ident = { "efi_fb" };
121 static struct vis_identifier text_ident = { "efi_text" };
122 
123 struct visual_ops fb_ops = {
124 	.ident = &fb_ident,
125 	.kdsetmode = NULL,
126 	.devinit = efi_fb_devinit,
127 	.cons_copy = NULL,
128 	.cons_display = NULL,
129 	.cons_cursor = efi_cons_cursor,
130 	.cons_clear = NULL,
131 	.cons_put_cmap = NULL
132 };
133 
134 struct visual_ops text_ops = {
135 	.ident = &text_ident,
136 	.kdsetmode = NULL,
137 	.devinit = efi_text_devinit,
138 	.cons_copy = efi_text_cons_copy,
139 	.cons_display = efi_text_cons_display,
140 	.cons_cursor = efi_cons_cursor,
141 	.cons_clear = efi_text_cons_clear,
142 	.cons_put_cmap = NULL
143 };
144 
145 /*
146  * platform specific functions for tem
147  */
148 int
149 plat_stdout_is_framebuffer(void)
150 {
151 	return (console_mode == EfiConsoleControlScreenGraphics);
152 }
153 
154 void
155 plat_tem_hide_prom_cursor(void)
156 {
157 	conout->EnableCursor(conout, FALSE);
158 }
159 
160 static void
161 plat_tem_display_prom_cursor(screen_pos_t row, screen_pos_t col)
162 {
163 
164 	conout->SetCursorPosition(conout, col, row);
165 	conout->EnableCursor(conout, TRUE);
166 }
167 
168 void
169 plat_tem_get_prom_pos(uint32_t *row, uint32_t *col)
170 {
171 	if (console_mode == EfiConsoleControlScreenText) {
172 		*col = (uint32_t)conout->Mode->CursorColumn;
173 		*row = (uint32_t)conout->Mode->CursorRow;
174 	} else {
175 		*col = 0;
176 		*row = 0;
177 	}
178 }
179 
180 /*
181  * plat_tem_get_prom_size() is supposed to return screen size
182  * in chars. Return real data for text mode and TEM defaults for graphical
183  * mode, so the tem can compute values based on default and font.
184  */
185 void
186 plat_tem_get_prom_size(size_t *height, size_t *width)
187 {
188 	UINTN cols, rows;
189 	if (console_mode == EfiConsoleControlScreenText) {
190 		(void) conout->QueryMode(conout, conout->Mode->Mode,
191 		    &cols, &rows);
192 		*height = (size_t)rows;
193 		*width = (size_t)cols;
194 	} else {
195 		*height = TEM_DEFAULT_ROWS;
196 		*width = TEM_DEFAULT_COLS;
197 	}
198 }
199 
200 /*
201  * Callback to notify about console mode change.
202  * mode is value from enum EFI_CONSOLE_CONTROL_SCREEN_MODE.
203  */
204 void
205 plat_cons_update_mode(int mode)
206 {
207 	UINTN cols, rows;
208 	struct vis_devinit devinit;
209 	struct efi_console_data *ecd = efi_console.c_private;
210 
211 	/* Make sure we have usable console. */
212 	if (efi_find_framebuffer(&efifb)) {
213 		console_mode = EfiConsoleControlScreenText;
214 	} else {
215 		efi_framebuffer_setup();
216 		if (mode != -1 && console_mode != mode)
217 			console_mode = mode;
218 	}
219 
220 	if (console_control != NULL)
221 		(void) console_control->SetMode(console_control, console_mode);
222 
223 	/* some firmware enables the cursor when switching modes */
224 	conout->EnableCursor(conout, FALSE);
225 	if (console_mode == EfiConsoleControlScreenText) {
226 		(void) conout->QueryMode(conout, conout->Mode->Mode,
227 		    &cols, &rows);
228 		devinit.version = VIS_CONS_REV;
229 		devinit.width = cols;
230 		devinit.height = rows;
231 		devinit.depth = 4;
232 		devinit.linebytes = cols;
233 		devinit.color_map = NULL;
234 		devinit.mode = VIS_TEXT;
235 		ecd->ecd_visual_ops = &text_ops;
236 	} else {
237 		devinit.version = VIS_CONS_REV;
238 		devinit.width = gfx_fb.framebuffer_common.framebuffer_width;
239 		devinit.height = gfx_fb.framebuffer_common.framebuffer_height;
240 		devinit.depth = gfx_fb.framebuffer_common.framebuffer_bpp;
241 		devinit.linebytes = gfx_fb.framebuffer_common.framebuffer_pitch;
242 		devinit.color_map = gfx_fb_color_map;
243 		devinit.mode = VIS_PIXEL;
244 		ecd->ecd_visual_ops = &fb_ops;
245 	}
246 
247 	modechg_cb(modechg_arg, &devinit);
248 }
249 
250 static int
251 efi_fb_devinit(struct vis_devinit *data)
252 {
253 	if (console_mode != EfiConsoleControlScreenGraphics)
254 		return (1);
255 
256 	data->version = VIS_CONS_REV;
257 	data->width = gfx_fb.framebuffer_common.framebuffer_width;
258 	data->height = gfx_fb.framebuffer_common.framebuffer_height;
259 	data->depth = gfx_fb.framebuffer_common.framebuffer_bpp;
260 	data->linebytes = gfx_fb.framebuffer_common.framebuffer_pitch;
261 	data->color_map = gfx_fb_color_map;
262 	data->mode = VIS_PIXEL;
263 
264 	modechg_cb = data->modechg_cb;
265 	modechg_arg = data->modechg_arg;
266 
267 	return (0);
268 }
269 
270 static int
271 efi_text_devinit(struct vis_devinit *data)
272 {
273 	UINTN cols, rows;
274 
275 	if (console_mode != EfiConsoleControlScreenText)
276 		return (1);
277 
278 	(void) conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows);
279 	data->version = VIS_CONS_REV;
280 	data->width = cols;
281 	data->height = rows;
282 	data->depth = 4;
283 	data->linebytes = cols;
284 	data->color_map = NULL;
285 	data->mode = VIS_TEXT;
286 
287 	modechg_cb = data->modechg_cb;
288 	modechg_arg = data->modechg_arg;
289 
290 	return (0);
291 }
292 
293 static int
294 efi_text_cons_clear(struct vis_consclear *ca)
295 {
296 	EFI_STATUS st;
297 	UINTN attr = conout->Mode->Attribute & 0x0F;
298 	uint8_t bg;
299 
300 	bg = solaris_color_to_efi_color[ca->bg_color & 0xF] & 0x7;
301 
302 	attr = EFI_TEXT_ATTR(attr, bg);
303 	st = conout->SetAttribute(conout, attr);
304 	if (EFI_ERROR(st))
305 		return (1);
306 	st = conout->ClearScreen(conout);
307 	if (EFI_ERROR(st))
308 		return (1);
309 	return (0);
310 }
311 
312 static void
313 efi_text_cons_copy(struct vis_conscopy *ma)
314 {
315 	UINTN col, row;
316 
317 	col = 0;
318 	row = ma->e_row;
319 	conout->SetCursorPosition(conout, col, row);
320 
321 	efi_cons_efiputchar('\n');
322 }
323 
324 static void
325 efi_text_cons_display(struct vis_consdisplay *da)
326 {
327 	EFI_STATUS st;
328 	UINTN attr;
329 	UINTN row, col;
330 	tem_char_t *data;
331 	uint8_t fg, bg;
332 	int i;
333 
334 	(void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row);
335 
336 	/* reduce clear line on bottom row by one to prevent autoscroll */
337 	if (row - 1 == da->row && da->col == 0 && da->width == col)
338 		da->width--;
339 
340 	data = (tem_char_t *)da->data;
341 	fg = solaris_color_to_efi_color[da->fg_color & 0xf];
342 	bg = solaris_color_to_efi_color[da->bg_color & 0xf] & 0x7;
343 	attr = EFI_TEXT_ATTR(fg, bg);
344 
345 	st = conout->SetAttribute(conout, attr);
346 	if (EFI_ERROR(st))
347 		return;
348 	row = da->row;
349 	col = da->col;
350 	conout->SetCursorPosition(conout, col, row);
351 	for (i = 0; i < da->width; i++)
352 		efi_cons_efiputchar(data[i]);
353 }
354 
355 static void efi_cons_cursor(struct vis_conscursor *cc)
356 {
357 	switch (cc->action) {
358 	case VIS_HIDE_CURSOR:
359 		if (plat_stdout_is_framebuffer())
360 			gfx_fb_display_cursor(cc);
361 		else
362 			plat_tem_hide_prom_cursor();
363 		break;
364 	case VIS_DISPLAY_CURSOR:
365 		if (plat_stdout_is_framebuffer())
366 			gfx_fb_display_cursor(cc);
367 		else
368 			plat_tem_display_prom_cursor(cc->row, cc->col);
369 		break;
370 	case VIS_GET_CURSOR: {	/* only used at startup */
371 		uint32_t row, col;
372 
373 		row = col = 0;
374 		plat_tem_get_prom_pos(&row, &col);
375 		cc->row = row;
376 		cc->col = col;
377 		}
378 		break;
379 	}
380 }
381 
382 static int
383 efi_cons_ioctl(struct console *cp, int cmd, void *data)
384 {
385 	struct efi_console_data *ecd = cp->c_private;
386 	struct visual_ops *ops = ecd->ecd_visual_ops;
387 
388 	switch (cmd) {
389 	case VIS_GETIDENTIFIER:
390 		memmove(data, ops->ident, sizeof (struct vis_identifier));
391 		break;
392 	case VIS_DEVINIT:
393 		return (ops->devinit(data));
394 	case VIS_CONSCLEAR:
395 		return (ops->cons_clear(data));
396 	case VIS_CONSCOPY:
397 		ops->cons_copy(data);
398 		break;
399 	case VIS_CONSDISPLAY:
400 		ops->cons_display(data);
401 		break;
402 	case VIS_CONSCURSOR:
403 		ops->cons_cursor(data);
404 		break;
405 	default:
406 		return (EINVAL);
407 	}
408 	return (0);
409 }
410 
411 static void
412 efi_framebuffer_setup(void)
413 {
414 	int bpp, pos;
415 
416 	bpp = fls(efifb.fb_mask_red | efifb.fb_mask_green |
417 	    efifb.fb_mask_blue | efifb.fb_mask_reserved);
418 
419 	gfx_fb.framebuffer_common.mb_type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
420 	gfx_fb.framebuffer_common.mb_size = sizeof (gfx_fb);
421 	gfx_fb.framebuffer_common.framebuffer_addr = efifb.fb_addr;
422 	gfx_fb.framebuffer_common.framebuffer_width = efifb.fb_width;
423 	gfx_fb.framebuffer_common.framebuffer_height = efifb.fb_height;
424 	gfx_fb.framebuffer_common.framebuffer_bpp = bpp;
425 	gfx_fb.framebuffer_common.framebuffer_pitch =
426 	    efifb.fb_stride * (bpp >> 3);
427 	gfx_fb.framebuffer_common.framebuffer_type =
428 	    MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
429 	gfx_fb.framebuffer_common.mb_reserved = 0;
430 
431 	pos = ffs(efifb.fb_mask_red);
432 	if (pos != 0)
433 		pos--;
434 	gfx_fb.u.fb2.framebuffer_red_mask_size = fls(efifb.fb_mask_red >> pos);
435 	gfx_fb.u.fb2.framebuffer_red_field_position = pos;
436 	pos = ffs(efifb.fb_mask_green);
437 	if (pos != 0)
438 		pos--;
439 	gfx_fb.u.fb2.framebuffer_green_mask_size =
440 	    fls(efifb.fb_mask_green >> pos);
441 	gfx_fb.u.fb2.framebuffer_green_field_position = pos;
442 	pos = ffs(efifb.fb_mask_blue);
443 	if (pos != 0)
444 		pos--;
445 	gfx_fb.u.fb2.framebuffer_blue_mask_size =
446 	    fls(efifb.fb_mask_blue >> pos);
447 	gfx_fb.u.fb2.framebuffer_blue_field_position = pos;
448 }
449 
450 static void
451 efi_cons_probe(struct console *cp)
452 {
453 	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
454 }
455 
456 static int
457 efi_cons_init(struct console *cp, int arg __unused)
458 {
459 	struct efi_console_data *ecd;
460 	void *coninex;
461 	EFI_STATUS status;
462 	UINTN i, max_dim, best_mode, cols, rows;
463 
464 	if (cp->c_private != NULL)
465 		return (0);
466 
467 	ecd = calloc(1, sizeof (*ecd));
468 	/*
469 	 * As console probing is called very early, the only reason for
470 	 * out of memory can be that we just do not have enough memory.
471 	 */
472 	if (ecd == NULL)
473 		panic("efi_cons_probe: This system has not enough memory\n");
474 	cp->c_private = ecd;
475 
476 	ecd->ecd_conin = ST->ConIn;
477 	conout = ST->ConOut;
478 
479 	conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
480 	    DEFAULT_BGCOLOR));
481 	memset(keybuf, 0, KEYBUFSZ);
482 
483 	status = BS->LocateProtocol(&ccontrol_protocol_guid, NULL,
484 	    (void **)&console_control);
485 	if (status == EFI_SUCCESS) {
486 		BOOLEAN GopUgaExists, StdInLocked;
487 		status = console_control->GetMode(console_control,
488 		    &console_mode, &GopUgaExists, &StdInLocked);
489 	} else {
490 		console_mode = EfiConsoleControlScreenText;
491 	}
492 
493 	max_dim = best_mode = 0;
494 	for (i = 0; i <= conout->Mode->MaxMode; i++) {
495 		status = conout->QueryMode(conout, i, &cols, &rows);
496 		if (EFI_ERROR(status))
497 			continue;
498 		if (cols * rows > max_dim) {
499 			max_dim = cols * rows;
500 			best_mode = i;
501 		}
502 	}
503 	if (max_dim > 0)
504 		conout->SetMode(conout, best_mode);
505 	status = conout->QueryMode(conout, best_mode, &cols, &rows);
506 	if (EFI_ERROR(status)) {
507 		setenv("screen-#rows", "24", 1);
508 		setenv("screen-#cols", "80", 1);
509 	} else {
510 		char env[8];
511 		snprintf(env, sizeof (env), "%u", (unsigned)rows);
512 		setenv("screen-#rows", env, 1);
513 		snprintf(env, sizeof (env), "%u", (unsigned)cols);
514 		setenv("screen-#cols", env, 1);
515 	}
516 
517 	if (efi_find_framebuffer(&efifb)) {
518 		console_mode = EfiConsoleControlScreenText;
519 		ecd->ecd_visual_ops = &text_ops;
520 	} else {
521 		efi_framebuffer_setup();
522 		console_mode = EfiConsoleControlScreenGraphics;
523 		ecd->ecd_visual_ops = &fb_ops;
524 	}
525 
526 	if (console_control != NULL)
527 		(void) console_control->SetMode(console_control, console_mode);
528 
529 	/* some firmware enables the cursor when switching modes */
530 	conout->EnableCursor(conout, FALSE);
531 
532 	coninex = NULL;
533 	/*
534 	 * Try to set up for SimpleTextInputEx protocol. If not available,
535 	 * we will use SimpleTextInput protocol.
536 	 */
537 	status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid,
538 	    &coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
539 	if (status == EFI_SUCCESS)
540 		ecd->ecd_coninex = coninex;
541 
542 	gfx_framework_init(&fb_ops);
543 
544 	if (tem_info_init(cp) == 0 && tem == NULL) {
545 		tem = tem_init();
546 		if (tem != NULL)
547 			tem_activate(tem, B_TRUE);
548 	}
549 
550 	if (tem == NULL)
551 		panic("Failed to set up console terminal");
552 
553 	return (0);
554 }
555 
556 static void
557 efi_cons_putchar(struct console *cp __unused, int c)
558 {
559 	uint8_t buf = c;
560 
561 	/* make sure we have some console output, support for panic() */
562 	if (tem == NULL)
563 		efi_cons_efiputchar(c);
564 	else
565 		tem_write(tem, &buf, sizeof (buf));
566 }
567 
568 static int
569 keybuf_getchar(void)
570 {
571 	int i, c = 0;
572 
573 	for (i = 0; i < KEYBUFSZ; i++) {
574 		if (keybuf[i] != 0) {
575 			c = keybuf[i];
576 			keybuf[i] = 0;
577 			break;
578 		}
579 	}
580 
581 	return (c);
582 }
583 
584 static bool
585 keybuf_ischar(void)
586 {
587 	int i;
588 
589 	for (i = 0; i < KEYBUFSZ; i++) {
590 		if (keybuf[i] != 0)
591 			return (true);
592 	}
593 	return (false);
594 }
595 
596 /*
597  * We are not reading input before keybuf is empty, so we are safe
598  * just to fill keybuf from the beginning.
599  */
600 static void
601 keybuf_inschar(EFI_INPUT_KEY *key)
602 {
603 
604 	switch (key->ScanCode) {
605 	case SCAN_UP: /* UP */
606 		keybuf[0] = 0x1b;	/* esc */
607 		keybuf[1] = '[';
608 		keybuf[2] = 'A';
609 		break;
610 	case SCAN_DOWN: /* DOWN */
611 		keybuf[0] = 0x1b;	/* esc */
612 		keybuf[1] = '[';
613 		keybuf[2] = 'B';
614 		break;
615 	case SCAN_RIGHT: /* RIGHT */
616 		keybuf[0] = 0x1b;	/* esc */
617 		keybuf[1] = '[';
618 		keybuf[2] = 'C';
619 		break;
620 	case SCAN_LEFT: /* LEFT */
621 		keybuf[0] = 0x1b;	/* esc */
622 		keybuf[1] = '[';
623 		keybuf[2] = 'D';
624 		break;
625 	case SCAN_DELETE:
626 		keybuf[0] = CHAR_BACKSPACE;
627 		break;
628 	case SCAN_ESC:
629 		keybuf[0] = 0x1b;	/* esc */
630 		break;
631 	default:
632 		keybuf[0] = key->UnicodeChar;
633 		break;
634 	}
635 }
636 
637 static bool
638 efi_readkey(SIMPLE_INPUT_INTERFACE *conin)
639 {
640 	EFI_STATUS status;
641 	EFI_INPUT_KEY key;
642 
643 	status = conin->ReadKeyStroke(conin, &key);
644 	if (status == EFI_SUCCESS) {
645 		keybuf_inschar(&key);
646 		return (true);
647 	}
648 	return (false);
649 }
650 
651 static bool
652 efi_readkey_ex(EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex)
653 {
654 	EFI_STATUS status;
655 	EFI_INPUT_KEY *kp;
656 	EFI_KEY_DATA  key_data;
657 	uint32_t kss;
658 
659 	status = coninex->ReadKeyStrokeEx(coninex, &key_data);
660 	if (status == EFI_SUCCESS) {
661 		kss = key_data.KeyState.KeyShiftState;
662 		kp = &key_data.Key;
663 		if (kss & EFI_SHIFT_STATE_VALID) {
664 
665 			/*
666 			 * quick mapping to control chars, replace with
667 			 * map lookup later.
668 			 */
669 			if (kss & EFI_RIGHT_CONTROL_PRESSED ||
670 			    kss & EFI_LEFT_CONTROL_PRESSED) {
671 				if (kp->UnicodeChar >= 'a' &&
672 				    kp->UnicodeChar <= 'z') {
673 					kp->UnicodeChar -= 'a';
674 					kp->UnicodeChar++;
675 				}
676 			}
677 			if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
678 				return (false);
679 			keybuf_inschar(kp);
680 			return (true);
681 		}
682 	}
683 	return (false);
684 }
685 
686 static int
687 efi_cons_getchar(struct console *cp)
688 {
689 	struct efi_console_data *ecd;
690 	int c;
691 
692 	if ((c = keybuf_getchar()) != 0)
693 		return (c);
694 
695 	ecd = cp->c_private;
696 	key_pending = 0;
697 
698 	if (ecd->ecd_coninex == NULL) {
699 		if (efi_readkey(ecd->ecd_conin))
700 			return (keybuf_getchar());
701 	} else {
702 		if (efi_readkey_ex(ecd->ecd_coninex))
703 			return (keybuf_getchar());
704 	}
705 
706 	return (-1);
707 }
708 
709 static int
710 efi_cons_poll(struct console *cp)
711 {
712 	struct efi_console_data *ecd;
713 	EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
714 	SIMPLE_INPUT_INTERFACE *conin;
715 	EFI_STATUS status;
716 
717 	if (keybuf_ischar() || key_pending)
718 		return (1);
719 
720 	ecd = cp->c_private;
721 	coninex = ecd->ecd_coninex;
722 	conin = ecd->ecd_conin;
723 	/*
724 	 * Some EFI implementation (u-boot for example) do not support
725 	 * WaitForKey().
726 	 * CheckEvent() can clear the signaled state.
727 	 */
728 	if (coninex != NULL) {
729 		if (coninex->WaitForKeyEx == NULL)
730 			key_pending = efi_readkey_ex(coninex);
731 		else {
732 			status = BS->CheckEvent(coninex->WaitForKeyEx);
733 			key_pending = status == EFI_SUCCESS;
734 		}
735 	} else {
736 		if (conin->WaitForKey == NULL)
737 			key_pending = efi_readkey(conin);
738 		else {
739 			status = BS->CheckEvent(conin->WaitForKey);
740 			key_pending = status == EFI_SUCCESS;
741 		}
742 	}
743 
744 	return (key_pending);
745 }
746 
747 /* Plain direct access to EFI OutputString(). */
748 void
749 efi_cons_efiputchar(int c)
750 {
751 	CHAR16 buf[2];
752 	EFI_STATUS status;
753 
754 	buf[0] = c;
755 	buf[1] = 0;	/* terminate string */
756 
757 	status = conout->TestString(conout, buf);
758 	if (EFI_ERROR(status))
759 		buf[0] = '?';
760 	conout->OutputString(conout, buf);
761 }
762 
763 static void
764 efi_cons_devinfo_print(EFI_HANDLE handle)
765 {
766 	EFI_DEVICE_PATH *dp;
767 	CHAR16 *text;
768 
769 	dp = efi_lookup_devpath(handle);
770 	if (dp == NULL)
771 		return;
772 
773 	text = efi_devpath_name(dp);
774 	if (text == NULL)
775 		return;
776 
777 	printf("\t%S", text);
778 	efi_free_devpath_name(text);
779 }
780 
781 static void
782 efi_cons_devinfo(struct console *cp __unused)
783 {
784 	EFI_HANDLE *handles;
785 	UINTN nhandles;
786 	extern EFI_GUID gop_guid;
787 	extern EFI_GUID uga_guid;
788 	EFI_STATUS status;
789 
790 	if (gop != NULL)
791 		status = BS->LocateHandleBuffer(ByProtocol, &gop_guid, NULL,
792 		    &nhandles, &handles);
793 	else
794 		status = BS->LocateHandleBuffer(ByProtocol, &uga_guid, NULL,
795 		    &nhandles, &handles);
796 
797 	if (EFI_ERROR(status))
798 		return;
799 
800 	for (UINTN i = 0; i < nhandles; i++)
801 		efi_cons_devinfo_print(handles[i]);
802 
803 	BS->FreePool(handles);
804 }
805