Home | History | Annotate | Line # | Download | only in efiboot
eficons.c revision 1.14
      1 /*	$NetBSD: eficons.c,v 1.14 2023/09/14 03:05:15 rin Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2016 Kimihiro Nonaka <nonaka (at) netbsd.org>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/param.h>
     30 #include <sys/bitops.h>
     31 #include <sys/stdint.h>
     32 
     33 #include <comio_direct.h>
     34 
     35 #include "efiboot.h"
     36 
     37 #include "bootinfo.h"
     38 #include "vbe.h"
     39 
     40 #ifndef DEFAULT_GOP_MODE
     41 #define DEFAULT_GOP_MODE	"1024x768"
     42 #endif
     43 #define FALLBACK_GOP_MODE	0
     44 
     45 extern struct x86_boot_params boot_params;
     46 
     47 struct btinfo_console btinfo_console;
     48 
     49 static EFI_GRAPHICS_OUTPUT_PROTOCOL *efi_gop;
     50 static int efi_gop_mode = -1;
     51 static CHAR16 keybuf[16];
     52 static int keybuf_read = 0;
     53 static int keybuf_write = 0;
     54 
     55 static SERIAL_IO_INTERFACE *serios[4];
     56 static int default_comspeed =
     57 #if defined(CONSPEED)
     58     CONSPEED;
     59 #else
     60     9600;
     61 #endif
     62 static u_char serbuf[16];
     63 static int serbuf_read = 0;
     64 static int serbuf_write = 0;
     65 
     66 static int raw_com_addr = 0;
     67 
     68 static void eficons_init_video(void);
     69 static void efi_switch_video_to_text_mode(void);
     70 
     71 static int efi_cons_getc(void);
     72 static int efi_cons_putc(int);
     73 static int efi_cons_iskey(int);
     74 static int efi_cons_waitforinputevent(uint64_t);
     75 
     76 static void efi_com_probe(void);
     77 static bool efi_valid_com(int);
     78 static int efi_com_init(int, int);
     79 static int efi_com_getc(void);
     80 static int efi_com_putc(int);
     81 static int efi_com_status(int);
     82 static int efi_com_waitforinputevent(uint64_t);
     83 
     84 static int raw_com_init(int, int);
     85 static int raw_com_getc(void);
     86 static int raw_com_putc(int);
     87 static int raw_com_status(int);
     88 static int raw_com_waitforinputevent(uint64_t);
     89 
     90 static int efi_find_gop_mode(char *);
     91 
     92 static int iodev;
     93 static int (*internal_getchar)(void) = efi_cons_getc;
     94 static int (*internal_putchar)(int) = efi_cons_putc;
     95 static int (*internal_iskey)(int) = efi_cons_iskey;
     96 static int (*internal_waitforinputevent)(uint64_t) = efi_cons_waitforinputevent;
     97 
     98 static int
     99 getcomaddr(int idx)
    100 {
    101 	static const short comioport[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
    102 
    103 	if (idx < __arraycount(comioport))
    104 		return comioport[idx];
    105 	return 0;
    106 }
    107 
    108 /*
    109  * XXX only pass console parameters to kernel.
    110  */
    111 void
    112 efi_consinit(int dev, int ioport, int speed)
    113 {
    114 	int i;
    115 
    116 	btinfo_console.speed = default_comspeed;
    117 
    118 	switch (dev) {
    119 	case CONSDEV_AUTO:
    120 		for (i = 0; i < __arraycount(serios); i++) {
    121 			iodev = CONSDEV_COM0 + i;
    122 			if (!efi_valid_com(iodev))
    123 				continue;
    124 			btinfo_console.addr = getcomaddr(i);
    125 
    126 			efi_cons_putc('0' + i);
    127 			efi_com_init(btinfo_console.addr, btinfo_console.speed);
    128 			/* check for:
    129 			 *  1. successful output
    130 			 *  2. optionally, keypress within 7s
    131 			 */
    132 			if (efi_com_putc(':') &&
    133 			    efi_com_putc('-') &&
    134 			    efi_com_putc('(') &&
    135 			    awaitkey(7, 0))
    136 				goto ok;
    137 		}
    138 		goto nocom;
    139 ok:
    140 		break;
    141 
    142 	case CONSDEV_COM0:
    143 	case CONSDEV_COM1:
    144 	case CONSDEV_COM2:
    145 	case CONSDEV_COM3:
    146 		iodev = dev;
    147 		btinfo_console.addr = ioport;
    148 		if (btinfo_console.addr == 0)
    149 			btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
    150 		if (speed != 0)
    151 			btinfo_console.speed = speed;
    152 		efi_com_init(btinfo_console.addr, btinfo_console.speed);
    153 		break;
    154 
    155 	case CONSDEV_COM0KBD:
    156 	case CONSDEV_COM1KBD:
    157 	case CONSDEV_COM2KBD:
    158 	case CONSDEV_COM3KBD:
    159 		iodev = dev - CONSDEV_COM0KBD + CONSDEV_COM0;
    160 		btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
    161 
    162 		efi_cons_putc('0' + iodev - CONSDEV_COM0);
    163 		efi_com_init(btinfo_console.addr, btinfo_console.speed);
    164 		/* check for:
    165 		 *  1. successful output
    166 		 *  2. optionally, keypress within 7s
    167 		 */
    168 		if (efi_com_putc(':') &&
    169 		    efi_com_putc('-') &&
    170 		    efi_com_putc('(') &&
    171 		    awaitkey(7, 0))
    172 			goto kbd;
    173 		/*FALLTHROUGH*/
    174 	case CONSDEV_PC:
    175 	default:
    176 nocom:
    177 		iodev = CONSDEV_PC;
    178 		internal_putchar = efi_cons_putc;
    179 kbd:
    180 		internal_getchar = efi_cons_getc;
    181 		internal_iskey = efi_cons_iskey;
    182 		internal_waitforinputevent = efi_cons_waitforinputevent;
    183 		memset(keybuf, 0, sizeof(keybuf));
    184 		keybuf_read = keybuf_write = 0;
    185 		break;
    186 	}
    187 
    188 	strlcpy(btinfo_console.devname, iodev == CONSDEV_PC ? "pc" : "com", 16);
    189 }
    190 
    191 int
    192 cninit(void)
    193 {
    194 
    195 	efi_switch_video_to_text_mode();
    196 	eficons_init_video();
    197 	efi_com_probe();
    198 
    199 	efi_consinit(boot_params.bp_consdev, boot_params.bp_consaddr,
    200 	    boot_params.bp_conspeed);
    201 
    202 	return 0;
    203 }
    204 
    205 void
    206 efi_cons_show(void)
    207 {
    208 	const bool pc_is_console = strcmp(btinfo_console.devname, "pc") == 0;
    209 	const bool com_is_console = strcmp(btinfo_console.devname, "com") == 0;
    210 	bool first = true;
    211 	bool found = false;
    212 	int i;
    213 
    214 	if (efi_gop != NULL) {
    215 		printf("pc");
    216 		if (pc_is_console)
    217 			printf("*");
    218 		first = false;
    219 	}
    220 
    221 	for (i = 0; i < __arraycount(serios); i++) {
    222 		if (serios[i] != NULL) {
    223 			if (!first)
    224 				printf(" ");
    225 			first = false;
    226 
    227 			printf("com%d", i);
    228 			if (com_is_console &&
    229 			    btinfo_console.addr == getcomaddr(i)) {
    230 				printf(",%d*", btinfo_console.speed);
    231 				found = true;
    232 			}
    233 		}
    234 	}
    235 	if (!found && com_is_console) {
    236 		if (!first)
    237 			printf(" ");
    238 		first = false;
    239 
    240 		printf("com,0x%x,%d*", btinfo_console.addr,
    241 		    btinfo_console.speed);
    242 	}
    243 
    244 	printf("\n");
    245 }
    246 
    247 static int
    248 efi_cons_getc(void)
    249 {
    250 	EFI_STATUS status;
    251 	EFI_INPUT_KEY key;
    252 	int c;
    253 
    254 	if (keybuf_read != keybuf_write) {
    255 		c = keybuf[keybuf_read];
    256 		keybuf_read = (keybuf_read + 1) % __arraycount(keybuf);
    257 		return c;
    258 	}
    259 
    260 	status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
    261 	    &key);
    262 	while (status == EFI_NOT_READY) {
    263 		WaitForSingleEvent(ST->ConIn->WaitForKey, 0);
    264 		status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2,
    265 		    ST->ConIn, &key);
    266 	}
    267 	return key.UnicodeChar;
    268 }
    269 
    270 static int
    271 efi_cons_putc(int c)
    272 {
    273 	CHAR16 buf[2];
    274 
    275 	buf[0] = c;
    276 	buf[1] = 0;
    277 	Output(buf);
    278 
    279 	return 1;
    280 }
    281 
    282 /*ARGSUSED*/
    283 static int
    284 efi_cons_iskey(int intr)
    285 {
    286 	EFI_STATUS status;
    287 	EFI_INPUT_KEY key;
    288 
    289 	if (keybuf_read != keybuf_write)
    290 		return 1;
    291 
    292 	status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
    293 	    &key);
    294 	if (EFI_ERROR(status))
    295 		return 0;
    296 
    297 	keybuf[keybuf_write] = key.UnicodeChar;
    298 	keybuf_write = (keybuf_write + 1) % __arraycount(keybuf);
    299 	return 1;
    300 }
    301 
    302 static int
    303 efi_cons_waitforinputevent(uint64_t timeout)
    304 {
    305 	EFI_STATUS status;
    306 
    307 	status = WaitForSingleEvent(ST->ConIn->WaitForKey, timeout);
    308 	if (!EFI_ERROR(status))
    309 		return 0;
    310 	if (status == EFI_TIMEOUT)
    311 		return ETIMEDOUT;
    312 	return EINVAL;
    313 }
    314 
    315 int
    316 getchar(void)
    317 {
    318 
    319 	return internal_getchar();
    320 }
    321 
    322 void
    323 putchar(int c)
    324 {
    325 
    326 	if (c == '\n')
    327 		internal_putchar('\r');
    328 	internal_putchar(c);
    329 }
    330 
    331 int
    332 iskey(int intr)
    333 {
    334 
    335 	return internal_iskey(intr);
    336 }
    337 
    338 char
    339 awaitkey(int timeout, int tell)
    340 {
    341 	char c = 0;
    342 
    343 	for (;;) {
    344 		char numbuf[32];
    345 		int len;
    346 
    347 		if (tell && timeout) {
    348 			len = snprintf(numbuf, sizeof(numbuf), "%d seconds. ",
    349 			    timeout);
    350 			if (len > 0 && len < sizeof(numbuf)) {
    351 				char *p = numbuf;
    352 
    353 				printf("%s", numbuf);
    354 				while (*p)
    355 					*p++ = '\b';
    356 			}
    357 		}
    358 		if (iskey(1)) {
    359 			/* flush input buffer */
    360 			while (iskey(0))
    361 				c = getchar();
    362 			if (c == 0)
    363 				c = -1;
    364 			if (tell && timeout)
    365 				printf("%s", numbuf);
    366 			break;
    367 		}
    368 		if (timeout--)
    369 			internal_waitforinputevent(10000000);
    370 		else
    371 			break;
    372 		if (tell)
    373 			printf("%s", numbuf);
    374 	}
    375 
    376 	if (tell)
    377 		printf("0 seconds.     \n");
    378 
    379 	return c;
    380 }
    381 
    382 void
    383 clear_pc_screen(void)
    384 {
    385 
    386 	uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
    387 }
    388 
    389 static uint8_t
    390 getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
    391 {
    392 
    393 	switch (info->PixelFormat) {
    394 	case PixelBlueGreenRedReserved8BitPerColor:
    395 	case PixelRedGreenBlueReserved8BitPerColor:
    396 		return 32;
    397 
    398 	case PixelBitMask:
    399 		return fls32(info->PixelInformation.RedMask
    400 		    | info->PixelInformation.GreenMask
    401 		    | info->PixelInformation.BlueMask
    402 		    | info->PixelInformation.ReservedMask);
    403 
    404 	case PixelBltOnly:
    405 	case PixelFormatMax:
    406 		return 0;
    407 	}
    408 	return 0;
    409 }
    410 
    411 static void
    412 setpixelformat(UINT32 mask, uint8_t *num, uint8_t *pos)
    413 {
    414 	uint8_t n, p;
    415 
    416 	n = popcount32(mask);
    417 	p = ffs32(mask);
    418 	if (p > 0)
    419 		p--;
    420 
    421 	*num = n;
    422 	*pos = p;
    423 }
    424 
    425 static void
    426 bi_framebuffer(void)
    427 {
    428 	EFI_STATUS status;
    429 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    430 	struct btinfo_framebuffer fb;
    431 	INT32 bestmode;
    432 	UINTN sz;
    433 
    434 	if (efi_gop == NULL)
    435 		goto nofb;
    436 
    437 	if (efi_gop_mode >= 0) {
    438 		bestmode = efi_gop_mode;
    439 	} else {
    440 		/* If a mode has not been selected, choose a default */
    441 		bestmode = efi_find_gop_mode(DEFAULT_GOP_MODE);
    442 		if (bestmode == -1)
    443 			bestmode = FALLBACK_GOP_MODE;
    444 	}
    445 
    446 	status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop,
    447 	    bestmode);
    448 	if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode) {
    449 		printf("GOP setmode failed: %" PRIxMAX "\n",
    450 		    (uintmax_t)status);
    451 		goto nofb;
    452 	}
    453 
    454 	status = uefi_call_wrapper(efi_gop->QueryMode, 4,
    455 	    efi_gop, bestmode, &sz, &info);
    456 	if (EFI_ERROR(status)) {
    457 		printf("GOP querymode failed: %" PRIxMAX "\n",
    458 		    (uintmax_t)status);
    459 		goto nofb;
    460 	}
    461 
    462 	memset(&fb, 0, sizeof(fb));
    463 	fb.physaddr = efi_gop->Mode->FrameBufferBase;
    464 	fb.flags = 0;
    465 	fb.width = info->HorizontalResolution;
    466 	fb.height = info->VerticalResolution;
    467 	fb.depth = getdepth(info);
    468 	fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8);
    469 	fb.vbemode = 0;	/* XXX */
    470 
    471 	switch (info->PixelFormat) {
    472 	case PixelBlueGreenRedReserved8BitPerColor:
    473 		fb.rnum = 8;
    474 		fb.gnum = 8;
    475 		fb.bnum = 8;
    476 		fb.rpos = 16;
    477 		fb.gpos = 8;
    478 		fb.bpos = 0;
    479 		break;
    480 
    481 	case PixelRedGreenBlueReserved8BitPerColor:
    482 		fb.rnum = 8;
    483 		fb.gnum = 8;
    484 		fb.bnum = 8;
    485 		fb.rpos = 0;
    486 		fb.gpos = 8;
    487 		fb.bpos = 16;
    488 		break;
    489 
    490 	case PixelBitMask:
    491 		setpixelformat(info->PixelInformation.RedMask,
    492 		    &fb.rnum, &fb.rpos);
    493 		setpixelformat(info->PixelInformation.GreenMask,
    494 		    &fb.gnum, &fb.gpos);
    495 		setpixelformat(info->PixelInformation.BlueMask,
    496 		    &fb.bnum, &fb.bpos);
    497 		break;
    498 
    499 	case PixelBltOnly:
    500 	case PixelFormatMax:
    501 		panic("Error: invalid pixel format (%d)", info->PixelFormat);
    502 		break;
    503 	}
    504 
    505 	framebuffer_configure(&fb);
    506 	return;
    507 
    508 nofb:
    509 	framebuffer_configure(NULL);
    510 }
    511 
    512 int
    513 vbe_commit(void)
    514 {
    515 
    516 	bi_framebuffer();
    517 	return 0;
    518 }
    519 
    520 static void
    521 print_text_modes(void)
    522 {
    523 	EFI_STATUS status;
    524 	UINTN cols, rows;
    525 	INT32 i, curmode;
    526 
    527 	curmode = ST->ConOut->Mode->Mode;
    528 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    529 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    530 		    ST->ConOut, i, &cols, &rows);
    531 		if (EFI_ERROR(status))
    532 			continue;
    533 		printf("%c%d: %" PRIxMAX "x%" PRIxMAX "\n",
    534 		    i == curmode ? '*' : ' ', i, (uintmax_t)cols, (uintmax_t)rows);
    535 	}
    536 }
    537 
    538 static int
    539 efi_find_text_mode(char *arg)
    540 {
    541 	EFI_STATUS status;
    542 	UINTN cols, rows;
    543 	INT32 i;
    544 	char mode[32];
    545 
    546 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    547 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    548 		    ST->ConOut, i, &cols, &rows);
    549 		if (EFI_ERROR(status))
    550 			continue;
    551 		snprintf(mode, sizeof(mode), "%" PRIuMAX "x%" PRIuMAX,
    552 		    (uintmax_t)cols, (uintmax_t)rows);
    553 		if (strcmp(arg, mode) == 0)
    554 			return i;
    555 	}
    556 	return -1;
    557 }
    558 
    559 void
    560 command_text(char *arg)
    561 {
    562 	EFI_STATUS status;
    563 	INT32 modenum;
    564 
    565 	if (*arg == '\0' || strcmp(arg, "list") == 0) {
    566 		print_text_modes();
    567 		return;
    568 	}
    569 
    570 	if (strchr(arg, 'x') != NULL) {
    571 		modenum = efi_find_text_mode(arg);
    572 		if (modenum == -1) {
    573 			printf("mode %s not supported by firmware\n", arg);
    574 			return;
    575 		}
    576 	} else {
    577 		modenum = strtoul(arg, NULL, 0);
    578 	}
    579 
    580 	status = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, modenum);
    581 	if (!EFI_ERROR(status))
    582 		return;
    583 
    584 	printf("invalid flag, must be 'list', a display mode, "
    585 	    "or a mode number\n");
    586 }
    587 
    588 static int
    589 print_gop_modes(void)
    590 {
    591 	EFI_STATUS status;
    592 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    593 	UINTN sz;
    594 	UINT32 i;
    595 	uint8_t depth;
    596 
    597 	if (efi_gop == NULL)
    598 		return 1;
    599 
    600 	for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    601 		status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
    602 		    &sz, &info);
    603 		if (EFI_ERROR(status) && status == EFI_NOT_STARTED) {
    604 			status = uefi_call_wrapper(efi_gop->SetMode, 2,
    605 			    efi_gop, efi_gop->Mode->Mode);
    606 			status = uefi_call_wrapper(efi_gop->QueryMode, 4,
    607 			    efi_gop, i, &sz, &info);
    608 		}
    609 		if (EFI_ERROR(status))
    610 			continue;
    611 
    612 		printf("%c%d: %dx%d ",
    613 		    memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ?
    614 		      '*' : ' ',
    615 		      i, info->HorizontalResolution, info->VerticalResolution);
    616 		switch (info->PixelFormat) {
    617 		case PixelRedGreenBlueReserved8BitPerColor:
    618 			printf("RGBR");
    619 			break;
    620 		case PixelBlueGreenRedReserved8BitPerColor:
    621 			printf("BGRR");
    622 			break;
    623 		case PixelBitMask:
    624 			printf("R:%08x G:%08x B:%08x X:%08x",
    625 			    info->PixelInformation.RedMask,
    626 			    info->PixelInformation.GreenMask,
    627 			    info->PixelInformation.BlueMask,
    628 			    info->PixelInformation.ReservedMask);
    629 			break;
    630 		case PixelBltOnly:
    631 			printf("(blt only)");
    632 			break;
    633 		default:
    634 			printf("(Invalid pixel format)");
    635 			break;
    636 		}
    637 		printf(" pitch %d", info->PixelsPerScanLine);
    638 		depth = getdepth(info);
    639 		if (depth > 0)
    640 			printf(" bpp %d", depth);
    641 		printf("\n");
    642 	}
    643 
    644 	return 0;
    645 }
    646 
    647 static int
    648 efi_find_gop_mode(char *arg)
    649 {
    650 	EFI_STATUS status;
    651 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    652 	UINTN sz;
    653 	UINT32 i;
    654 	char mode[32];
    655 	uint8_t depth;
    656 
    657 	for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    658 		status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
    659 		    &sz, &info);
    660 		if (EFI_ERROR(status))
    661 			continue;
    662 
    663 		depth = getdepth(info);
    664 		if (depth == 0)
    665 			continue;
    666 
    667 		snprintf(mode, sizeof(mode), "%lux%lux%u",
    668 		    (long)info->HorizontalResolution,
    669 		    (long)info->VerticalResolution,
    670 		    depth);
    671 		if (strcmp(arg, mode) == 0)
    672 			return i;
    673 
    674 		snprintf(mode, sizeof(mode), "%lux%lu",
    675 		    (long)info->HorizontalResolution,
    676 		    (long)info->VerticalResolution);
    677 		if (strcmp(arg, mode) == 0)
    678 			return i;
    679 	}
    680 	return -1;
    681 }
    682 
    683 void
    684 command_gop(char *arg)
    685 {
    686 	EFI_STATUS status;
    687 	INT32 modenum;
    688 
    689 	if (efi_gop == NULL) {
    690 		printf("GOP not supported by firmware\n");
    691 		return;
    692 	}
    693 
    694 	if (*arg == '\0' || strcmp(arg, "list") == 0) {
    695 		print_gop_modes();
    696 		return;
    697 	}
    698 
    699 	if (strchr(arg, 'x') != NULL) {
    700 		modenum = efi_find_gop_mode(arg);
    701 		if (modenum == -1) {
    702 			printf("mode %s not supported by firmware\n", arg);
    703 			return;
    704 		}
    705 	} else {
    706 		modenum = strtoul(arg, NULL, 0);
    707 	}
    708 
    709 	status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum);
    710 	if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) {
    711 		efi_gop_mode = modenum;
    712 		return;
    713 	}
    714 
    715 	printf("invalid flag, must be 'list', a display mode, "
    716 	    "or a mode number\n");
    717 }
    718 
    719 static void
    720 eficons_init_video(void)
    721 {
    722 	EFI_STATUS status;
    723 	UINTN cols, rows;
    724 	INT32 i, best, mode80x25, mode100x31;
    725 
    726 	/*
    727 	 * Setup text mode
    728 	 */
    729 	uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE);
    730 
    731 	mode80x25 = mode100x31 = -1;
    732 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    733 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    734 		    ST->ConOut, i, &cols, &rows);
    735 		if (EFI_ERROR(status))
    736 			continue;
    737 
    738 		if (mode80x25 < 0 && cols == 80 && rows == 25)
    739 			mode80x25 = i;
    740 		else if (mode100x31 < 0 && cols == 100 && rows == 31)
    741 			mode100x31 = i;
    742 	}
    743 	best = mode100x31 >= 0 ? mode100x31 : mode80x25 >= 0 ? mode80x25 : -1;
    744 	if (best >= 0)
    745 		uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best);
    746 	uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
    747 	uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
    748 
    749 	LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop);
    750 }
    751 
    752 /*
    753  * for Apple EFI
    754  */
    755 #define	CONSOLE_CONTROL_PROTOCOL \
    756 	{0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}}
    757 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL;
    758 
    759 struct _EFI_CONSOLE_CONTROL_INTERFACE;
    760 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE;
    761 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE;
    762 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) (
    763 	IN EFI_CONSOLE_CONTROL_INTERFACE *This,
    764 	IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
    765 );
    766 struct _EFI_CONSOLE_CONTROL_INTERFACE {
    767 	VOID *GetMode;
    768 	EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
    769 	VOID *LockStdIn;
    770 };
    771 
    772 static void
    773 efi_switch_video_to_text_mode(void)
    774 {
    775 	EFI_STATUS status;
    776 	EFI_CONSOLE_CONTROL_INTERFACE *cci;
    777 
    778 	/* Set up the console, so printf works. */
    779 	status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci);
    780 	if (!EFI_ERROR(status)) {
    781 		uefi_call_wrapper(cci->SetMode, 2, cci,
    782 		    EfiConsoleControlScreenText);
    783 	}
    784 }
    785 
    786 /*
    787  * serial port
    788  */
    789 static void
    790 efi_com_probe(void)
    791 {
    792 	EFI_STATUS status;
    793 	UINTN i, nhandles;
    794 	EFI_HANDLE *handles;
    795 	EFI_DEVICE_PATH	*dp, *dp0;
    796 	EFI_DEV_PATH_PTR dpp;
    797 	SERIAL_IO_INTERFACE *serio;
    798 	int uid = -1;
    799 
    800 	status = LibLocateHandle(ByProtocol, &SerialIoProtocol, NULL,
    801 	    &nhandles, &handles);
    802 	if (EFI_ERROR(status))
    803 		return;
    804 
    805 	for (i = 0; i < nhandles; i++) {
    806 		/*
    807 		 * Identify port number of the handle.  This assumes ACPI
    808 		 * UID 0-3 map to legacy COM[1-4] and they use the legacy
    809 		 * port address.
    810 		 */
    811 		status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
    812 		    &DevicePathProtocol, (void **)&dp0);
    813 		if (EFI_ERROR(status))
    814 			continue;
    815 
    816 		for (uid = -1, dp = dp0;
    817 		     !IsDevicePathEnd(dp);
    818 		     dp = NextDevicePathNode(dp)) {
    819 
    820 			if (DevicePathType(dp) == ACPI_DEVICE_PATH &&
    821 			    DevicePathSubType(dp) == ACPI_DP) {
    822 				dpp = (EFI_DEV_PATH_PTR)dp;
    823 				if (dpp.Acpi->HID == EISA_PNP_ID(0x0501)) {
    824 					uid = dpp.Acpi->UID;
    825 					break;
    826 				}
    827 			}
    828 		}
    829 		if (uid < 0 || __arraycount(serios) <= uid)
    830 			continue;
    831 
    832 		/* Prepare SERIAL_IO_INTERFACE */
    833 		status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
    834 		    &SerialIoProtocol, (void **)&serio);
    835 		if (EFI_ERROR(status))
    836 			continue;
    837 
    838 		serios[uid] = serio;
    839 	}
    840 
    841 	FreePool(handles);
    842 
    843 }
    844 
    845 static bool
    846 efi_valid_com(int dev)
    847 {
    848 	int idx;
    849 
    850 	switch (dev) {
    851 	default:
    852 	case CONSDEV_PC:
    853 		return false;
    854 
    855 	case CONSDEV_COM0:
    856 	case CONSDEV_COM1:
    857 	case CONSDEV_COM2:
    858 	case CONSDEV_COM3:
    859 		idx = dev - CONSDEV_COM0;
    860 		break;
    861 	}
    862 
    863 	return idx < __arraycount(serios) &&
    864 	    serios[idx] != NULL &&
    865 	    getcomaddr(idx) != 0;
    866 }
    867 
    868 static int
    869 efi_com_init(int addr, int speed)
    870 {
    871 	EFI_STATUS status;
    872 	SERIAL_IO_INTERFACE *serio;
    873 
    874 	if (speed <= 0)
    875 		return 0;
    876 
    877 	if (!efi_valid_com(iodev))
    878 		return raw_com_init(addr, speed);
    879 
    880 	serio = serios[iodev - CONSDEV_COM0];
    881 
    882 	if (serio->Mode->BaudRate != btinfo_console.speed) {
    883 		status = uefi_call_wrapper(serio->SetAttributes, 7, serio,
    884 		    speed, serio->Mode->ReceiveFifoDepth,
    885 		    serio->Mode->Timeout, serio->Mode->Parity,
    886 		    serio->Mode->DataBits, serio->Mode->StopBits);
    887 		if (EFI_ERROR(status)) {
    888 			printf("com%d: SetAttribute() failed with status=%" PRIxMAX
    889 			    "\n", iodev - CONSDEV_COM0, (uintmax_t)status);
    890 			return 0;
    891 		}
    892 	}
    893 
    894 	raw_com_addr = 0;
    895 	default_comspeed = speed;
    896 	internal_getchar = efi_com_getc;
    897 	internal_putchar = efi_com_putc;
    898 	internal_iskey = efi_com_status;
    899 	internal_waitforinputevent = efi_com_waitforinputevent;
    900 	memset(serbuf, 0, sizeof(serbuf));
    901 	serbuf_read = serbuf_write = 0;
    902 
    903 	return speed;
    904 }
    905 
    906 static int
    907 efi_com_getc(void)
    908 {
    909 	EFI_STATUS status;
    910 	SERIAL_IO_INTERFACE *serio;
    911 	UINTN sz;
    912 	u_char c;
    913 
    914 	if (!efi_valid_com(iodev))
    915 		panic("Invalid serial port: iodev=%d", iodev);
    916 
    917 	if (serbuf_read != serbuf_write) {
    918 		c = serbuf[serbuf_read];
    919 		serbuf_read = (serbuf_read + 1) % __arraycount(serbuf);
    920 		return c;
    921 	}
    922 
    923 	serio = serios[iodev - CONSDEV_COM0];
    924 
    925 	for (;;) {
    926 		sz = 1;
    927 		status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
    928 		if (!EFI_ERROR(status) && sz > 0)
    929 			break;
    930 		if (status != EFI_TIMEOUT && EFI_ERROR(status))
    931 			panic("Error reading from serial status=%"PRIxMAX,
    932 			    (uintmax_t)status);
    933 	}
    934 	return c;
    935 }
    936 
    937 static int
    938 efi_com_putc(int c)
    939 {
    940 	EFI_STATUS status;
    941 	SERIAL_IO_INTERFACE *serio;
    942 	UINTN sz = 1;
    943 	u_char buf;
    944 
    945 	if (!efi_valid_com(iodev))
    946 		return 0;
    947 
    948 	serio = serios[iodev - CONSDEV_COM0];
    949 	buf = c;
    950 	status = uefi_call_wrapper(serio->Write, 3, serio, &sz, &buf);
    951 	if (EFI_ERROR(status) || sz < 1)
    952 		return 0;
    953 	return 1;
    954 }
    955 
    956 /*ARGSUSED*/
    957 static int
    958 efi_com_status(int intr)
    959 {
    960 	EFI_STATUS status;
    961 	SERIAL_IO_INTERFACE *serio;
    962 	UINTN sz;
    963 	u_char c;
    964 
    965 	if (!efi_valid_com(iodev))
    966 		panic("Invalid serial port: iodev=%d", iodev);
    967 
    968 	if (serbuf_read != serbuf_write)
    969 		return 1;
    970 
    971 	serio = serios[iodev - CONSDEV_COM0];
    972 	sz = 1;
    973 	status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
    974 	if (EFI_ERROR(status) || sz < 1)
    975 		return 0;
    976 
    977 	serbuf[serbuf_write] = c;
    978 	serbuf_write = (serbuf_write + 1) % __arraycount(serbuf);
    979 	return 1;
    980 }
    981 
    982 static void
    983 efi_com_periodic_event(EFI_EVENT event, void *ctx)
    984 {
    985 	EFI_EVENT timer = ctx;
    986 
    987 	if (efi_com_status(0)) {
    988 		uefi_call_wrapper(BS->SetTimer, 3, event, TimerCancel, 0);
    989 		uefi_call_wrapper(BS->SignalEvent, 1, timer);
    990 	}
    991 }
    992 
    993 static int
    994 efi_com_waitforinputevent(uint64_t timeout)
    995 {
    996 	EFI_STATUS status;
    997 	EFI_EVENT timer, periodic;
    998 
    999 	status = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL,
   1000 	    &timer);
   1001 	if (EFI_ERROR(status))
   1002 		return EINVAL;
   1003 
   1004         status = uefi_call_wrapper(BS->CreateEvent, 5,
   1005 	    EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_com_periodic_event,
   1006 	    timer, &periodic);
   1007 	if (EFI_ERROR(status)) {
   1008 		uefi_call_wrapper(BS->CloseEvent, 1, timer);
   1009 		return EINVAL;
   1010 	}
   1011 
   1012 	status = uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerPeriodic,
   1013 	    1000000);	/* 100ms */
   1014 	if (EFI_ERROR(status)) {
   1015 		uefi_call_wrapper(BS->CloseEvent, 1, periodic);
   1016 		uefi_call_wrapper(BS->CloseEvent, 1, timer);
   1017 		return EINVAL;
   1018 	}
   1019 	status = WaitForSingleEvent(&timer, timeout);
   1020 	uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerCancel, 0);
   1021 	uefi_call_wrapper(BS->CloseEvent, 1, periodic);
   1022 	uefi_call_wrapper(BS->CloseEvent, 1, timer);
   1023 	if (!EFI_ERROR(status))
   1024 		return 0;
   1025 	if (status == EFI_TIMEOUT)
   1026 		return ETIMEDOUT;
   1027 	return EINVAL;
   1028 }
   1029 
   1030 static int
   1031 raw_com_init(int addr, int speed)
   1032 {
   1033 
   1034 	if (addr == 0 || speed <= 0)
   1035 		return 0;
   1036 
   1037 	speed = cominit_d(addr, speed);
   1038 
   1039 	raw_com_addr = addr;
   1040 	default_comspeed = speed;
   1041 	internal_getchar = raw_com_getc;
   1042 	internal_putchar = raw_com_putc;
   1043 	internal_iskey = raw_com_status;
   1044 	internal_waitforinputevent = raw_com_waitforinputevent;
   1045 
   1046 	return speed;
   1047 }
   1048 
   1049 static int
   1050 raw_com_getc(void)
   1051 {
   1052 
   1053 	if (raw_com_addr == 0)
   1054 		panic("%s: Invalid serial port", __func__);
   1055 	return comgetc_d(raw_com_addr);
   1056 }
   1057 
   1058 static int
   1059 raw_com_putc(int c)
   1060 {
   1061 
   1062 	if (raw_com_addr == 0)
   1063 		panic("%s: Invalid serial port", __func__);
   1064 	return computc_d(c, raw_com_addr);
   1065 }
   1066 
   1067 static int
   1068 raw_com_status(int intr)
   1069 {
   1070 
   1071 	if (raw_com_addr == 0)
   1072 		panic("%s: Invalid serial port", __func__);
   1073 	return comstatus_d(raw_com_addr);
   1074 }
   1075 
   1076 static int
   1077 raw_com_waitforinputevent(uint64_t timeout /* in 0.1 usec */)
   1078 {
   1079 	uint64_t ms;
   1080 
   1081 	if (raw_com_addr == 0)
   1082 		panic("%s: Invalid serial port", __func__);
   1083 
   1084 	for (ms = howmany(timeout, 10 * 1000); ms != 0; ms--) {
   1085 		if (raw_com_status(0))
   1086 			return 0;
   1087 		uefi_call_wrapper(BS->Stall, 1, 1000);
   1088 	}
   1089 	return ETIMEDOUT;
   1090 }
   1091