Home | History | Annotate | Line # | Download | only in efiboot
eficons.c revision 1.4.10.1
      1 /*	$NetBSD: eficons.c,v 1.4.10.1 2018/03/30 06:20:11 pgoyette 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 		if (tell && timeout) {
    332 			char numbuf[32];
    333 			int len;
    334 
    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 				printf("%s", numbuf);
    344 			}
    345 		}
    346 		if (iskey(1)) {
    347 			/* flush input buffer */
    348 			while (iskey(0))
    349 				c = getchar();
    350 			if (c == 0)
    351 				c = -1;
    352 			goto out;
    353 		}
    354 		if (timeout--)
    355 			internal_waitforinputevent(10000000);
    356 		else
    357 			break;
    358 	}
    359 
    360 out:
    361 	if (tell)
    362 		printf("0 seconds.     \n");
    363 
    364 	return c;
    365 }
    366 
    367 void
    368 clear_pc_screen(void)
    369 {
    370 
    371 	uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
    372 }
    373 
    374 static uint8_t
    375 getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
    376 {
    377 
    378 	switch (info->PixelFormat) {
    379 	case PixelBlueGreenRedReserved8BitPerColor:
    380 	case PixelRedGreenBlueReserved8BitPerColor:
    381 		return 32;
    382 
    383 	case PixelBitMask:
    384 		return fls32(info->PixelInformation.RedMask
    385 		    | info->PixelInformation.GreenMask
    386 		    | info->PixelInformation.BlueMask
    387 		    | info->PixelInformation.ReservedMask);
    388 
    389 	case PixelBltOnly:
    390 	case PixelFormatMax:
    391 		return 0;
    392 	}
    393 	return 0;
    394 }
    395 
    396 static void
    397 setpixelformat(UINT32 mask, uint8_t *num, uint8_t *pos)
    398 {
    399 	uint8_t n, p;
    400 
    401 	n = popcount32(mask);
    402 	p = ffs32(mask);
    403 	if (p > 0)
    404 		p--;
    405 
    406 	*num = n;
    407 	*pos = p;
    408 }
    409 
    410 static void
    411 bi_framebuffer(void)
    412 {
    413 	EFI_STATUS status;
    414 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    415 	struct btinfo_framebuffer fb;
    416 	INT32 bestmode = -1;
    417 
    418 	if (efi_gop == NULL) {
    419 		framebuffer_configure(NULL);
    420 		return;
    421 	}
    422 
    423 	if (efi_gop_mode >= 0) {
    424 		bestmode = efi_gop_mode;
    425 	} else {
    426 #if 0
    427 		UINT64 res, bestres = 0;
    428 		UINTN sz;
    429 		UINT32 i;
    430 
    431 		/* XXX EDID? EFI_EDID_DISCOVERED_PROTOCOL */
    432 		for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    433 			status = uefi_call_wrapper(efi_gop->QueryMode, 4,
    434 			    efi_gop, i, &sz, &info);
    435 			if (EFI_ERROR(status))
    436 				continue;
    437 
    438 			res = (UINT64)info->HorizontalResolution *
    439 			    (UINT64)info->VerticalResolution *
    440 			    (UINT64)getdepth(info);
    441 			if (res > bestres) {
    442 				bestmode = i;
    443 				bestres = res;
    444 			}
    445 		}
    446 #endif
    447 	}
    448 	if (bestmode >= 0) {
    449 		status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop,
    450 		    bestmode);
    451 		if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode)
    452 			printf("GOP setmode failed: %" PRIxMAX "\n",
    453 			    (uintmax_t)status);
    454 	}
    455 
    456 	info = efi_gop->Mode->Info;
    457 	memset(&fb, 0, sizeof(fb));
    458 	fb.physaddr = efi_gop->Mode->FrameBufferBase;
    459 	fb.flags = 0;
    460 	fb.width = info->HorizontalResolution;
    461 	fb.height = info->VerticalResolution;
    462 	fb.depth = getdepth(info);
    463 	fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8);
    464 	fb.vbemode = 0;	/* XXX */
    465 
    466 	switch (info->PixelFormat) {
    467 	case PixelBlueGreenRedReserved8BitPerColor:
    468 		fb.rnum = 8;
    469 		fb.gnum = 8;
    470 		fb.bnum = 8;
    471 		fb.rpos = 16;
    472 		fb.gpos = 8;
    473 		fb.bpos = 0;
    474 		break;
    475 
    476 	case PixelRedGreenBlueReserved8BitPerColor:
    477 		fb.rnum = 8;
    478 		fb.gnum = 8;
    479 		fb.bnum = 8;
    480 		fb.rpos = 0;
    481 		fb.gpos = 8;
    482 		fb.bpos = 16;
    483 		break;
    484 
    485 	case PixelBitMask:
    486 		setpixelformat(info->PixelInformation.RedMask,
    487 		    &fb.rnum, &fb.rpos);
    488 		setpixelformat(info->PixelInformation.GreenMask,
    489 		    &fb.gnum, &fb.gpos);
    490 		setpixelformat(info->PixelInformation.BlueMask,
    491 		    &fb.bnum, &fb.bpos);
    492 		break;
    493 
    494 	case PixelBltOnly:
    495 	case PixelFormatMax:
    496 		panic("Error: invalid pixel format (%d)", info->PixelFormat);
    497 		break;
    498 	}
    499 
    500 	framebuffer_configure(&fb);
    501 }
    502 
    503 int
    504 vbe_commit(void)
    505 {
    506 
    507 	bi_framebuffer();
    508 	return 0;
    509 }
    510 
    511 static void
    512 print_text_modes(void)
    513 {
    514 	EFI_STATUS status;
    515 	UINTN cols, rows;
    516 	INT32 i, curmode;
    517 
    518 	curmode = ST->ConOut->Mode->Mode;
    519 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    520 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    521 		    ST->ConOut, i, &cols, &rows);
    522 		if (EFI_ERROR(status))
    523 			continue;
    524 		printf("%c%d: %" PRIxMAX "x%" PRIxMAX "\n",
    525 		    i == curmode ? '*' : ' ', i, (uintmax_t)cols, (uintmax_t)rows);
    526 	}
    527 }
    528 
    529 static int
    530 efi_find_text_mode(char *arg)
    531 {
    532 	EFI_STATUS status;
    533 	UINTN cols, rows;
    534 	INT32 i;
    535 	char mode[32];
    536 
    537 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    538 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    539 		    ST->ConOut, i, &cols, &rows);
    540 		if (EFI_ERROR(status))
    541 			continue;
    542 		snprintf(mode, sizeof(mode), "%" PRIuMAX "x%" PRIuMAX,
    543 		    (uintmax_t)cols, (uintmax_t)rows);
    544 		if (strcmp(arg, mode) == 0)
    545 			return i;
    546 	}
    547 	return -1;
    548 }
    549 
    550 void
    551 command_text(char *arg)
    552 {
    553 	EFI_STATUS status;
    554 	INT32 modenum;
    555 
    556 	if (*arg == '\0' || strcmp(arg, "list") == 0) {
    557 		print_text_modes();
    558 		return;
    559 	}
    560 
    561 	if (strchr(arg, 'x') != NULL) {
    562 		modenum = efi_find_text_mode(arg);
    563 		if (modenum == -1) {
    564 			printf("mode %s not supported by firmware\n", arg);
    565 			return;
    566 		}
    567 	} else {
    568 		modenum = strtoul(arg, NULL, 0);
    569 	}
    570 
    571 	status = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, modenum);
    572 	if (!EFI_ERROR(status))
    573 		return;
    574 
    575 	printf("invalid flag, must be 'list', a display mode, "
    576 	    "or a mode number\n");
    577 }
    578 
    579 static int
    580 print_gop_modes(void)
    581 {
    582 	EFI_STATUS status;
    583 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    584 	UINTN sz;
    585 	UINT32 i;
    586 	uint8_t depth;
    587 
    588 	if (efi_gop == NULL)
    589 		return 1;
    590 
    591 	for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    592 		status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
    593 		    &sz, &info);
    594 		if (EFI_ERROR(status) && status == EFI_NOT_STARTED) {
    595 			status = uefi_call_wrapper(efi_gop->SetMode, 2,
    596 			    efi_gop, efi_gop->Mode->Mode);
    597 			status = uefi_call_wrapper(efi_gop->QueryMode, 4,
    598 			    efi_gop, i, &sz, &info);
    599 		}
    600 		if (EFI_ERROR(status))
    601 			continue;
    602 
    603 		printf("%c%d: %dx%d ",
    604 		    memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ?
    605 		      '*' : ' ',
    606 		      i, info->HorizontalResolution, info->VerticalResolution);
    607 		switch (info->PixelFormat) {
    608 		case PixelRedGreenBlueReserved8BitPerColor:
    609 			printf("RGBR");
    610 			break;
    611 		case PixelBlueGreenRedReserved8BitPerColor:
    612 			printf("BGRR");
    613 			break;
    614 		case PixelBitMask:
    615 			printf("R:%08x G:%08x B:%08x X:%08x",
    616 			    info->PixelInformation.RedMask,
    617 			    info->PixelInformation.GreenMask,
    618 			    info->PixelInformation.BlueMask,
    619 			    info->PixelInformation.ReservedMask);
    620 			break;
    621 		case PixelBltOnly:
    622 			printf("(blt only)");
    623 			break;
    624 		default:
    625 			printf("(Invalid pixel format)");
    626 			break;
    627 		}
    628 		printf(" pitch %d", info->PixelsPerScanLine);
    629 		depth = getdepth(info);
    630 		if (depth > 0)
    631 			printf(" bpp %d", depth);
    632 		printf("\n");
    633 	}
    634 
    635 	return 0;
    636 }
    637 
    638 static int
    639 efi_find_gop_mode(char *arg)
    640 {
    641 	EFI_STATUS status;
    642 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    643 	UINTN sz;
    644 	UINT32 i;
    645 	char mode[32];
    646 	uint8_t depth;
    647 
    648 	for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    649 		status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
    650 		    &sz, &info);
    651 		if (EFI_ERROR(status))
    652 			continue;
    653 
    654 		depth = getdepth(info);
    655 		if (depth == 0)
    656 			continue;
    657 
    658 		snprintf(mode, sizeof(mode), "%lux%lux%u",
    659 		    (long)info->HorizontalResolution,
    660 		    (long)info->HorizontalResolution,
    661 		    depth);
    662 		if (strcmp(arg, mode) == 0)
    663 			return i;
    664 	}
    665 	return -1;
    666 }
    667 
    668 void
    669 command_gop(char *arg)
    670 {
    671 	EFI_STATUS status;
    672 	INT32 modenum;
    673 
    674 	if (efi_gop == NULL) {
    675 		printf("GOP not supported by firmware\n");
    676 		return;
    677 	}
    678 
    679 	if (*arg == '\0' || strcmp(arg, "list") == 0) {
    680 		print_gop_modes();
    681 		return;
    682 	}
    683 
    684 	if (strchr(arg, 'x') != NULL) {
    685 		modenum = efi_find_gop_mode(arg);
    686 		if (modenum == -1) {
    687 			printf("mode %s not supported by firmware\n", arg);
    688 			return;
    689 		}
    690 	} else {
    691 		modenum = strtoul(arg, NULL, 0);
    692 	}
    693 
    694 	status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum);
    695 	if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) {
    696 		efi_gop_mode = modenum;
    697 		return;
    698 	}
    699 
    700 	printf("invalid flag, must be 'list', a display mode, "
    701 	    "or a mode number\n");
    702 }
    703 
    704 static void
    705 eficons_init_video(void)
    706 {
    707 	EFI_STATUS status;
    708 	UINTN cols, rows;
    709 	INT32 i, best, mode80x25, mode100x31;
    710 
    711 	/*
    712 	 * Setup text mode
    713 	 */
    714 	uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE);
    715 
    716 	mode80x25 = mode100x31 = -1;
    717 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    718 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    719 		    ST->ConOut, i, &cols, &rows);
    720 		if (EFI_ERROR(status))
    721 			continue;
    722 
    723 		if (mode80x25 < 0 && cols == 80 && rows == 25)
    724 			mode80x25 = i;
    725 		else if (mode100x31 < 0 && cols == 100 && rows == 31)
    726 			mode100x31 = i;
    727 	}
    728 	best = mode100x31 >= 0 ? mode100x31 : mode80x25 >= 0 ? mode80x25 : -1;
    729 	if (best >= 0)
    730 		uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best);
    731 	uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
    732 	uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
    733 
    734 	LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop);
    735 }
    736 
    737 /*
    738  * for Apple EFI
    739  */
    740 #define	CONSOLE_CONTROL_PROTOCOL \
    741 	{0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}}
    742 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL;
    743 
    744 struct _EFI_CONSOLE_CONTROL_INTERFACE;
    745 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE;
    746 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE;
    747 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) (
    748 	IN EFI_CONSOLE_CONTROL_INTERFACE *This,
    749 	IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
    750 );
    751 struct _EFI_CONSOLE_CONTROL_INTERFACE {
    752 	VOID *GetMode;
    753 	EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
    754 	VOID *LockStdIn;
    755 };
    756 
    757 static void
    758 efi_switch_video_to_text_mode(void)
    759 {
    760 	EFI_STATUS status;
    761 	EFI_CONSOLE_CONTROL_INTERFACE *cci;
    762 
    763 	/* Set up the console, so printf works. */
    764 	status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci);
    765 	if (!EFI_ERROR(status)) {
    766 		uefi_call_wrapper(cci->SetMode, 2, cci,
    767 		    EfiConsoleControlScreenText);
    768 	}
    769 }
    770 
    771 /*
    772  * serial port
    773  */
    774 static void
    775 efi_com_probe(void)
    776 {
    777 	EFI_STATUS status;
    778 	UINTN i, nhandles;
    779 	EFI_HANDLE *handles;
    780 	EFI_DEVICE_PATH	*dp, *dp0;
    781 	EFI_DEV_PATH_PTR dpp;
    782 	SERIAL_IO_INTERFACE *serio;
    783 	int uid = -1;
    784 
    785 	status = LibLocateHandle(ByProtocol, &SerialIoProtocol, NULL,
    786 	    &nhandles, &handles);
    787 	if (EFI_ERROR(status))
    788 		return;
    789 
    790 	for (i = 0; i < nhandles; i++) {
    791 		/*
    792 		 * Identify port number of the handle.  This assumes ACPI
    793 		 * UID 0-3 map to legacy COM[1-4] and they use the legacy
    794 		 * port address.
    795 		 */
    796 		status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
    797 		    &DevicePathProtocol, (void **)&dp0);
    798 		if (EFI_ERROR(status))
    799 			continue;
    800 
    801 		for (uid = -1, dp = dp0;
    802 		     !IsDevicePathEnd(dp);
    803 		     dp = NextDevicePathNode(dp)) {
    804 
    805 			if (DevicePathType(dp) == ACPI_DEVICE_PATH &&
    806 			    DevicePathSubType(dp) == ACPI_DP) {
    807 				dpp = (EFI_DEV_PATH_PTR)dp;
    808 				if (dpp.Acpi->HID == EISA_PNP_ID(0x0501)) {
    809 					uid = dpp.Acpi->UID;
    810 					break;
    811 				}
    812 			}
    813 		}
    814 		if (uid < 0 || __arraycount(serios) <= uid)
    815 			continue;
    816 
    817 		/* Prepare SERIAL_IO_INTERFACE */
    818 		status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
    819 		    &SerialIoProtocol, (void **)&serio);
    820 		if (EFI_ERROR(status))
    821 			continue;
    822 
    823 		serios[uid] = serio;
    824 	}
    825 
    826 	FreePool(handles);
    827 
    828 }
    829 
    830 static bool
    831 efi_valid_com(int dev)
    832 {
    833 	int idx;
    834 
    835 	switch (dev) {
    836 	default:
    837 	case CONSDEV_PC:
    838 		return false;
    839 
    840 	case CONSDEV_COM0:
    841 	case CONSDEV_COM1:
    842 	case CONSDEV_COM2:
    843 	case CONSDEV_COM3:
    844 		idx = dev - CONSDEV_COM0;
    845 		break;
    846 	}
    847 
    848 	return idx < __arraycount(serios) &&
    849 	    serios[idx] != NULL &&
    850 	    getcomaddr(idx) != 0;
    851 }
    852 
    853 static int
    854 efi_com_init(int addr, int speed)
    855 {
    856 	EFI_STATUS status;
    857 	SERIAL_IO_INTERFACE *serio;
    858 
    859 	if (speed <= 0)
    860 		return 0;
    861 
    862 	if (!efi_valid_com(iodev))
    863 		return 0;
    864 
    865 	serio = serios[iodev - CONSDEV_COM0];
    866 
    867 	if (serio->Mode->BaudRate != btinfo_console.speed) {
    868 		status = uefi_call_wrapper(serio->SetAttributes, 7, serio,
    869 		    speed, serio->Mode->ReceiveFifoDepth,
    870 		    serio->Mode->Timeout, serio->Mode->Parity,
    871 		    serio->Mode->DataBits, serio->Mode->StopBits);
    872 		if (EFI_ERROR(status)) {
    873 			printf("com%d: SetAttribute() failed with status=%" PRIxMAX
    874 			    "\n", iodev - CONSDEV_COM0, (uintmax_t)status);
    875 			return 0;
    876 		}
    877 	}
    878 
    879 	default_comspeed = speed;
    880 	internal_getchar = efi_com_getc;
    881 	internal_putchar = efi_com_putc;
    882 	internal_iskey = efi_com_status;
    883 	internal_waitforinputevent = efi_com_waitforinputevent;
    884 	memset(serbuf, 0, sizeof(serbuf));
    885 	serbuf_read = serbuf_write = 0;
    886 
    887 	return speed;
    888 }
    889 
    890 static int
    891 efi_com_getc(void)
    892 {
    893 	EFI_STATUS status;
    894 	SERIAL_IO_INTERFACE *serio;
    895 	UINTN sz;
    896 	u_char c;
    897 
    898 	if (!efi_valid_com(iodev))
    899 		panic("Invalid serial port: iodev=%d", iodev);
    900 
    901 	if (serbuf_read != serbuf_write) {
    902 		c = serbuf[serbuf_read];
    903 		serbuf_read = (serbuf_read + 1) % __arraycount(serbuf);
    904 		return c;
    905 	}
    906 
    907 	serio = serios[iodev - CONSDEV_COM0];
    908 
    909 	for (;;) {
    910 		sz = 1;
    911 		status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
    912 		if (!EFI_ERROR(status) && sz > 0)
    913 			break;
    914 		if (status != EFI_TIMEOUT && EFI_ERROR(status))
    915 			panic("Error reading from serial status=%"PRIxMAX,
    916 			    (uintmax_t)status);
    917 	}
    918 	return c;
    919 }
    920 
    921 static int
    922 efi_com_putc(int c)
    923 {
    924 	EFI_STATUS status;
    925 	SERIAL_IO_INTERFACE *serio;
    926 	UINTN sz = 1;
    927 	u_char buf;
    928 
    929 	if (!efi_valid_com(iodev))
    930 		return 0;
    931 
    932 	serio = serios[iodev - CONSDEV_COM0];
    933 	buf = c;
    934 	status = uefi_call_wrapper(serio->Write, 3, serio, &sz, &buf);
    935 	if (EFI_ERROR(status) || sz < 1)
    936 		return 0;
    937 	return 1;
    938 }
    939 
    940 /*ARGSUSED*/
    941 static int
    942 efi_com_status(int intr)
    943 {
    944 	EFI_STATUS status;
    945 	SERIAL_IO_INTERFACE *serio;
    946 	UINTN sz;
    947 	u_char c;
    948 
    949 	if (!efi_valid_com(iodev))
    950 		panic("Invalid serial port: iodev=%d", iodev);
    951 
    952 	if (serbuf_read != serbuf_write)
    953 		return 1;
    954 
    955 	serio = serios[iodev - CONSDEV_COM0];
    956 	sz = 1;
    957 	status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
    958 	if (EFI_ERROR(status) || sz < 1)
    959 		return 0;
    960 
    961 	serbuf[serbuf_write] = c;
    962 	serbuf_write = (serbuf_write + 1) % __arraycount(serbuf);
    963 	return 1;
    964 }
    965 
    966 static void
    967 efi_com_periodic_event(EFI_EVENT event, void *ctx)
    968 {
    969 	EFI_EVENT timer = ctx;
    970 
    971 	if (efi_com_status(0)) {
    972 		uefi_call_wrapper(BS->SetTimer, 3, event, TimerCancel, 0);
    973 		uefi_call_wrapper(BS->SignalEvent, 1, timer);
    974 	}
    975 }
    976 
    977 static int
    978 efi_com_waitforinputevent(uint64_t timeout)
    979 {
    980 	EFI_STATUS status;
    981 	EFI_EVENT timer, periodic;
    982 
    983 	status = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL,
    984 	    &timer);
    985 	if (EFI_ERROR(status))
    986 		return EINVAL;
    987 
    988         status = uefi_call_wrapper(BS->CreateEvent, 5,
    989 	    EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_com_periodic_event,
    990 	    timer, &periodic);
    991 	if (EFI_ERROR(status)) {
    992 		uefi_call_wrapper(BS->CloseEvent, 1, timer);
    993 		return EINVAL;
    994 	}
    995 
    996 	status = uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerPeriodic,
    997 	    1000000);	/* 100ms */
    998 	if (EFI_ERROR(status)) {
    999 		uefi_call_wrapper(BS->CloseEvent, 1, periodic);
   1000 		uefi_call_wrapper(BS->CloseEvent, 1, timer);
   1001 		return EINVAL;
   1002 	}
   1003 	status = WaitForSingleEvent(&timer, timeout);
   1004 	uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerCancel, 0);
   1005 	uefi_call_wrapper(BS->CloseEvent, 1, periodic);
   1006 	uefi_call_wrapper(BS->CloseEvent, 1, timer);
   1007 	if (!EFI_ERROR(status))
   1008 		return 0;
   1009 	if (status == EFI_TIMEOUT)
   1010 		return ETIMEDOUT;
   1011 	return EINVAL;
   1012 }
   1013