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