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