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
38struct efi_fb		efifb;
39EFI_GRAPHICS_OUTPUT	*gop;
40EFI_UGA_DRAW_PROTOCOL	*uga;
41
42static EFI_GUID ccontrol_protocol_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
43static EFI_CONSOLE_CONTROL_PROTOCOL	*console_control;
44static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
45static EFI_CONSOLE_CONTROL_SCREEN_MODE	console_mode;
46static SIMPLE_TEXT_OUTPUT_INTERFACE	*conout;
47
48/* mode change callback and argument from tem */
49static vis_modechg_cb_t modechg_cb;
50static struct vis_modechg_arg *modechg_arg;
51static tem_vt_state_t tem;
52
53struct 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
60static unsigned keybuf[KEYBUFSZ];	/* keybuf for extended codes */
61
62static int key_pending;
63
64static 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
86extern int efi_find_framebuffer(struct efi_fb *efifb);
87
88static void efi_framebuffer_setup(void);
89static void efi_cons_probe(struct console *);
90static int efi_cons_init(struct console *, int);
91static void efi_cons_putchar(struct console *, int);
92static void efi_cons_efiputchar(int);
93static int efi_cons_getchar(struct console *);
94static int efi_cons_poll(struct console *);
95static int efi_cons_ioctl(struct console *cp, int cmd, void *data);
96static void efi_cons_devinfo(struct console *);
97
98static int efi_fb_devinit(struct vis_devinit *);
99static void efi_cons_cursor(struct vis_conscursor *);
100
101static int efi_text_devinit(struct vis_devinit *);
102static int efi_text_cons_clear(struct vis_consclear *);
103static void efi_text_cons_copy(struct vis_conscopy *);
104static void efi_text_cons_display(struct vis_consdisplay *);
105
106struct 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
120static struct vis_identifier fb_ident = { "efi_fb" };
121static struct vis_identifier text_ident = { "efi_text" };
122
123struct 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
134struct 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 */
148int
149plat_stdout_is_framebuffer(void)
150{
151	return (console_mode == EfiConsoleControlScreenGraphics);
152}
153
154void
155plat_tem_hide_prom_cursor(void)
156{
157	conout->EnableCursor(conout, FALSE);
158}
159
160static void
161plat_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
168void
169plat_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 */
185void
186plat_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 */
204void
205plat_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
250static int
251efi_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
270static int
271efi_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
293static int
294efi_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
312static void
313efi_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
324static void
325efi_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
355static 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
382static int
383efi_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
411static void
412efi_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
450static void
451efi_cons_probe(struct console *cp)
452{
453	cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
454}
455
456static int
457efi_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
556static void
557efi_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
568static int
569keybuf_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
584static bool
585keybuf_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 */
600static void
601keybuf_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
637static bool
638efi_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
651static bool
652efi_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		}
678		/*
679		 * The shift state and/or toggle state may not be valid,
680		 * but we still can have ScanCode or UnicodeChar.
681		 */
682		if (kp->ScanCode == 0 && kp->UnicodeChar == 0)
683			return (false);
684		keybuf_inschar(kp);
685		return (true);
686	}
687	return (false);
688}
689
690static int
691efi_cons_getchar(struct console *cp)
692{
693	struct efi_console_data *ecd;
694	int c;
695
696	if ((c = keybuf_getchar()) != 0)
697		return (c);
698
699	ecd = cp->c_private;
700	key_pending = 0;
701
702	if (ecd->ecd_coninex == NULL) {
703		if (efi_readkey(ecd->ecd_conin))
704			return (keybuf_getchar());
705	} else {
706		if (efi_readkey_ex(ecd->ecd_coninex))
707			return (keybuf_getchar());
708	}
709
710	return (-1);
711}
712
713static int
714efi_cons_poll(struct console *cp)
715{
716	struct efi_console_data *ecd;
717	EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex;
718	SIMPLE_INPUT_INTERFACE *conin;
719	EFI_STATUS status;
720
721	if (keybuf_ischar() || key_pending)
722		return (1);
723
724	ecd = cp->c_private;
725	coninex = ecd->ecd_coninex;
726	conin = ecd->ecd_conin;
727	/*
728	 * Some EFI implementation (u-boot for example) do not support
729	 * WaitForKey().
730	 * CheckEvent() can clear the signaled state.
731	 */
732	if (coninex != NULL) {
733		if (coninex->WaitForKeyEx == NULL)
734			key_pending = efi_readkey_ex(coninex);
735		else {
736			status = BS->CheckEvent(coninex->WaitForKeyEx);
737			key_pending = status == EFI_SUCCESS;
738		}
739	} else {
740		if (conin->WaitForKey == NULL)
741			key_pending = efi_readkey(conin);
742		else {
743			status = BS->CheckEvent(conin->WaitForKey);
744			key_pending = status == EFI_SUCCESS;
745		}
746	}
747
748	return (key_pending);
749}
750
751/* Plain direct access to EFI OutputString(). */
752void
753efi_cons_efiputchar(int c)
754{
755	CHAR16 buf[2];
756	EFI_STATUS status;
757
758	buf[0] = c;
759	buf[1] = 0;	/* terminate string */
760
761	status = conout->TestString(conout, buf);
762	if (EFI_ERROR(status))
763		buf[0] = '?';
764	conout->OutputString(conout, buf);
765}
766
767static void
768efi_cons_devinfo_print(EFI_HANDLE handle)
769{
770	EFI_DEVICE_PATH *dp;
771	CHAR16 *text;
772
773	dp = efi_lookup_devpath(handle);
774	if (dp == NULL)
775		return;
776
777	text = efi_devpath_name(dp);
778	if (text == NULL)
779		return;
780
781	printf("\t%S", text);
782	efi_free_devpath_name(text);
783}
784
785static void
786efi_cons_devinfo(struct console *cp __unused)
787{
788	EFI_HANDLE *handles;
789	UINTN nhandles;
790	extern EFI_GUID gop_guid;
791	extern EFI_GUID uga_guid;
792	EFI_STATUS status;
793
794	if (gop != NULL)
795		status = BS->LocateHandleBuffer(ByProtocol, &gop_guid, NULL,
796		    &nhandles, &handles);
797	else
798		status = BS->LocateHandleBuffer(ByProtocol, &uga_guid, NULL,
799		    &nhandles, &handles);
800
801	if (EFI_ERROR(status))
802		return;
803
804	for (UINTN i = 0; i < nhandles; i++)
805		efi_cons_devinfo_print(handles[i]);
806
807	BS->FreePool(handles);
808}
809