Home | History | Annotate | Line # | Download | only in efiboot
eficons.c revision 1.4
      1 /*	$NetBSD: eficons.c,v 1.4 2017/05/01 13:03:01 nonaka 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 
     44 static CHAR16 keybuf[16];
     45 static int keybuf_read = 0;
     46 static int keybuf_write = 0;
     47 
     48 static void eficons_init_video(void);
     49 static void efi_switch_video_to_text_mode(void);
     50 
     51 static int
     52 getcomaddr(int idx)
     53 {
     54 	static const short comioport[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
     55 
     56 	if (idx < __arraycount(comioport))
     57 		return comioport[idx];
     58 	return 0;
     59 }
     60 
     61 /*
     62  * XXX only pass console parameters to kernel.
     63  */
     64 void
     65 consinit(int dev, int ioport, int speed)
     66 {
     67 	int iodev;
     68 
     69 #if defined(CONSPEED)
     70 	btinfo_console.speed = CONSPEED;
     71 #else
     72 	btinfo_console.speed = 9600;
     73 #endif
     74 
     75 	switch (dev) {
     76 	case CONSDEV_AUTO:
     77 		/* XXX comport */
     78 		goto nocom;
     79 
     80 	case CONSDEV_COM0:
     81 	case CONSDEV_COM1:
     82 	case CONSDEV_COM2:
     83 	case CONSDEV_COM3:
     84 		iodev = dev;
     85 comport:
     86 		btinfo_console.addr = ioport;
     87 		if (btinfo_console.addr == 0) {
     88 			btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
     89 			if (btinfo_console.addr == 0)
     90 				goto nocom;
     91 		}
     92 		if (speed != 0)
     93 			btinfo_console.speed = speed;
     94 		break;
     95 
     96 	case CONSDEV_COM0KBD:
     97 	case CONSDEV_COM1KBD:
     98 	case CONSDEV_COM2KBD:
     99 	case CONSDEV_COM3KBD:
    100 		iodev = dev - CONSDEV_COM0KBD + CONSDEV_COM0;
    101 		goto comport;	/* XXX kbd */
    102 
    103 	case CONSDEV_PC:
    104 	default:
    105 nocom:
    106 		iodev = CONSDEV_PC;
    107 		break;
    108 	}
    109 
    110 	strlcpy(btinfo_console.devname, iodev == CONSDEV_PC ? "pc" : "com", 16);
    111 }
    112 
    113 int
    114 cninit(void)
    115 {
    116 
    117 	efi_switch_video_to_text_mode();
    118 	eficons_init_video();
    119 
    120 	consinit(boot_params.bp_consdev, boot_params.bp_consaddr,
    121 	    boot_params.bp_conspeed);
    122 
    123 	return 0;
    124 }
    125 
    126 int
    127 getchar(void)
    128 {
    129 	EFI_STATUS status;
    130 	EFI_INPUT_KEY key;
    131 	int c;
    132 
    133 	if (keybuf_read != keybuf_write) {
    134 		c = keybuf[keybuf_read];
    135 		keybuf_read = (keybuf_read + 1) % __arraycount(keybuf);
    136 		return c;
    137 	}
    138 
    139 	status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
    140 	    &key);
    141 	while (status == EFI_NOT_READY) {
    142 		WaitForSingleEvent(ST->ConIn->WaitForKey, 0);
    143 		status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2,
    144 		    ST->ConIn, &key);
    145 	}
    146 	return key.UnicodeChar;
    147 }
    148 
    149 void
    150 putchar(int c)
    151 {
    152 	CHAR16 buf[2];
    153 
    154 	buf[1] = 0;
    155 	if (c == '\n') {
    156 		buf[0] = '\r';
    157 		Output(buf);
    158 	}
    159 	buf[0] = c;
    160 	Output(buf);
    161 }
    162 
    163 /*ARGSUSED*/
    164 int
    165 iskey(int intr)
    166 {
    167 	EFI_STATUS status;
    168 	EFI_INPUT_KEY key;
    169 
    170 	if (keybuf_read != keybuf_write)
    171 		return 1;
    172 
    173 	status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
    174 	    &key);
    175 	if (EFI_ERROR(status))
    176 		return 0;
    177 
    178 	keybuf[keybuf_write] = key.UnicodeChar;
    179 	keybuf_write = (keybuf_write + 1) % __arraycount(keybuf);
    180 	return 1;
    181 }
    182 
    183 char
    184 awaitkey(int timeout, int tell)
    185 {
    186 	char c = 0;
    187 
    188 	for (;;) {
    189 		if (tell) {
    190 			char numbuf[32];
    191 			int len;
    192 
    193 			len = snprintf(numbuf, sizeof(numbuf), "%d seconds. ",
    194 			    timeout);
    195 			if (len > 0 && len < sizeof(numbuf)) {
    196 				char *p = numbuf;
    197 
    198 				printf("%s", numbuf);
    199 				while (*p)
    200 					*p++ = '\b';
    201 				printf("%s", numbuf);
    202 			}
    203 		}
    204 		if (iskey(1)) {
    205 			/* flush input buffer */
    206 			while (iskey(0))
    207 				c = getchar();
    208 			if (c == 0)
    209 				c = -1;
    210 			goto out;
    211 		}
    212 		if (timeout--)
    213 			WaitForSingleEvent(ST->ConIn->WaitForKey, 10000000);
    214 		else
    215 			break;
    216 	}
    217 
    218 out:
    219 	if (tell)
    220 		printf("0 seconds.     \n");
    221 
    222 	return c;
    223 }
    224 
    225 void
    226 clear_pc_screen(void)
    227 {
    228 
    229 	uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
    230 }
    231 
    232 static uint8_t
    233 getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
    234 {
    235 
    236 	switch (info->PixelFormat) {
    237 	case PixelBlueGreenRedReserved8BitPerColor:
    238 	case PixelRedGreenBlueReserved8BitPerColor:
    239 		return 32;
    240 
    241 	case PixelBitMask:
    242 		return fls32(info->PixelInformation.RedMask
    243 		    | info->PixelInformation.GreenMask
    244 		    | info->PixelInformation.BlueMask
    245 		    | info->PixelInformation.ReservedMask);
    246 
    247 	case PixelBltOnly:
    248 	case PixelFormatMax:
    249 		return 0;
    250 	}
    251 	return 0;
    252 }
    253 
    254 static void
    255 setpixelformat(UINT32 mask, uint8_t *num, uint8_t *pos)
    256 {
    257 	uint8_t n, p;
    258 
    259 	n = popcount32(mask);
    260 	p = ffs32(mask);
    261 	if (p > 0)
    262 		p--;
    263 
    264 	*num = n;
    265 	*pos = p;
    266 }
    267 
    268 static void
    269 bi_framebuffer(void)
    270 {
    271 	EFI_STATUS status;
    272 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    273 	struct btinfo_framebuffer fb;
    274 	INT32 bestmode = -1;
    275 
    276 	if (efi_gop == NULL) {
    277 		framebuffer_configure(NULL);
    278 		return;
    279 	}
    280 
    281 	if (efi_gop_mode >= 0) {
    282 		bestmode = efi_gop_mode;
    283 	} else {
    284 #if 0
    285 		UINT64 res, bestres = 0;
    286 		UINTN sz;
    287 		UINT32 i;
    288 
    289 		/* XXX EDID? EFI_EDID_DISCOVERED_PROTOCOL */
    290 		for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    291 			status = uefi_call_wrapper(efi_gop->QueryMode, 4,
    292 			    efi_gop, i, &sz, &info);
    293 			if (EFI_ERROR(status))
    294 				continue;
    295 
    296 			res = (UINT64)info->HorizontalResolution *
    297 			    (UINT64)info->VerticalResolution *
    298 			    (UINT64)getdepth(info);
    299 			if (res > bestres) {
    300 				bestmode = i;
    301 				bestres = res;
    302 			}
    303 		}
    304 #endif
    305 	}
    306 	if (bestmode >= 0) {
    307 		status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop,
    308 		    bestmode);
    309 		if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode)
    310 			Print(L"GOP setmode failed: %r\n", status);
    311 	}
    312 
    313 	info = efi_gop->Mode->Info;
    314 	memset(&fb, 0, sizeof(fb));
    315 	fb.physaddr = efi_gop->Mode->FrameBufferBase;
    316 	fb.flags = 0;
    317 	fb.width = info->HorizontalResolution;
    318 	fb.height = info->VerticalResolution;
    319 	fb.depth = getdepth(info);
    320 	fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8);
    321 	fb.vbemode = 0;	/* XXX */
    322 
    323 	switch (info->PixelFormat) {
    324 	case PixelBlueGreenRedReserved8BitPerColor:
    325 		fb.rnum = 8;
    326 		fb.gnum = 8;
    327 		fb.bnum = 8;
    328 		fb.rpos = 16;
    329 		fb.gpos = 8;
    330 		fb.bpos = 0;
    331 		break;
    332 
    333 	case PixelRedGreenBlueReserved8BitPerColor:
    334 		fb.rnum = 8;
    335 		fb.gnum = 8;
    336 		fb.bnum = 8;
    337 		fb.rpos = 0;
    338 		fb.gpos = 8;
    339 		fb.bpos = 16;
    340 		break;
    341 
    342 	case PixelBitMask:
    343 		setpixelformat(info->PixelInformation.RedMask,
    344 		    &fb.rnum, &fb.rpos);
    345 		setpixelformat(info->PixelInformation.GreenMask,
    346 		    &fb.gnum, &fb.gpos);
    347 		setpixelformat(info->PixelInformation.BlueMask,
    348 		    &fb.bnum, &fb.bpos);
    349 		break;
    350 
    351 	case PixelBltOnly:
    352 	case PixelFormatMax:
    353 		Panic(L"Error: invalid pixel format (%d)", info->PixelFormat);
    354 		break;
    355 	}
    356 
    357 	framebuffer_configure(&fb);
    358 }
    359 
    360 int
    361 vbe_commit(void)
    362 {
    363 
    364 	bi_framebuffer();
    365 	return 0;
    366 }
    367 
    368 static void
    369 print_text_modes(void)
    370 {
    371 	EFI_STATUS status;
    372 	UINTN cols, rows;
    373 	INT32 i, curmode;
    374 
    375 	curmode = ST->ConOut->Mode->Mode;
    376 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    377 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    378 		    ST->ConOut, i, &cols, &rows);
    379 		if (EFI_ERROR(status))
    380 			continue;
    381 		Print(L"%c%d: %dx%d\n", i == curmode ? '*' : ' ',
    382 		    i, cols, rows);
    383 	}
    384 }
    385 
    386 static int
    387 efi_find_text_mode(char *arg)
    388 {
    389 	EFI_STATUS status;
    390 	UINTN cols, rows;
    391 	INT32 i;
    392 	char mode[32];
    393 
    394 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    395 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    396 		    ST->ConOut, i, &cols, &rows);
    397 		if (EFI_ERROR(status))
    398 			continue;
    399 		snprintf(mode, sizeof(mode), "%lux%lu", (long)cols, (long)rows);
    400 		if (strcmp(arg, mode) == 0)
    401 			return i;
    402 	}
    403 	return -1;
    404 }
    405 
    406 void
    407 command_text(char *arg)
    408 {
    409 	EFI_STATUS status;
    410 	INT32 modenum;
    411 
    412 	if (*arg == '\0' || strcmp(arg, "list") == 0) {
    413 		print_text_modes();
    414 		return;
    415 	}
    416 
    417 	if (strchr(arg, 'x') != NULL) {
    418 		modenum = efi_find_text_mode(arg);
    419 		if (modenum == -1) {
    420 			printf("mode %s not supported by firmware\n", arg);
    421 			return;
    422 		}
    423 	} else {
    424 		modenum = strtoul(arg, NULL, 0);
    425 	}
    426 
    427 	status = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, modenum);
    428 	if (!EFI_ERROR(status))
    429 		return;
    430 
    431 	printf("invalid flag, must be 'list', a display mode, "
    432 	    "or a mode number\n");
    433 }
    434 
    435 static int
    436 print_gop_modes(void)
    437 {
    438 	EFI_STATUS status;
    439 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    440 	UINTN sz;
    441 	UINT32 i;
    442 	uint8_t depth;
    443 
    444 	if (efi_gop == NULL)
    445 		return 1;
    446 
    447 	for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    448 		status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
    449 		    &sz, &info);
    450 		if (EFI_ERROR(status) && status == EFI_NOT_STARTED) {
    451 			status = uefi_call_wrapper(efi_gop->SetMode, 2,
    452 			    efi_gop, efi_gop->Mode->Mode);
    453 			status = uefi_call_wrapper(efi_gop->QueryMode, 4,
    454 			    efi_gop, i, &sz, &info);
    455 		}
    456 		if (EFI_ERROR(status))
    457 			continue;
    458 
    459 		Print(L"%c%d: %dx%d ",
    460 		    memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ?
    461 		      '*' : ' ',
    462 		      i, info->HorizontalResolution, info->VerticalResolution);
    463 		switch (info->PixelFormat) {
    464 		case PixelRedGreenBlueReserved8BitPerColor:
    465 			Print(L"RGBR");
    466 			break;
    467 		case PixelBlueGreenRedReserved8BitPerColor:
    468 			Print(L"BGRR");
    469 			break;
    470 		case PixelBitMask:
    471 			Print(L"R:%08x G:%08x B:%08x X:%08x",
    472 			    info->PixelInformation.RedMask,
    473 			    info->PixelInformation.GreenMask,
    474 			    info->PixelInformation.BlueMask,
    475 			    info->PixelInformation.ReservedMask);
    476 			break;
    477 		case PixelBltOnly:
    478 			Print(L"(blt only)");
    479 			break;
    480 		default:
    481 			Print(L"(Invalid pixel format)");
    482 			break;
    483 		}
    484 		Print(L" pitch %d", info->PixelsPerScanLine);
    485 		depth = getdepth(info);
    486 		if (depth > 0)
    487 			Print(L" bpp %d", depth);
    488 		Print(L"\n");
    489 	}
    490 
    491 	return 0;
    492 }
    493 
    494 static int
    495 efi_find_gop_mode(char *arg)
    496 {
    497 	EFI_STATUS status;
    498 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    499 	UINTN sz;
    500 	UINT32 i;
    501 	char mode[32];
    502 	uint8_t depth;
    503 
    504 	for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    505 		status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
    506 		    &sz, &info);
    507 		if (EFI_ERROR(status))
    508 			continue;
    509 
    510 		depth = getdepth(info);
    511 		if (depth == 0)
    512 			continue;
    513 
    514 		snprintf(mode, sizeof(mode), "%lux%lux%u",
    515 		    (long)info->HorizontalResolution,
    516 		    (long)info->HorizontalResolution,
    517 		    depth);
    518 		if (strcmp(arg, mode) == 0)
    519 			return i;
    520 	}
    521 	return -1;
    522 }
    523 
    524 void
    525 command_gop(char *arg)
    526 {
    527 	EFI_STATUS status;
    528 	INT32 modenum;
    529 
    530 	if (efi_gop == NULL) {
    531 		printf("GOP not supported by firmware\n");
    532 		return;
    533 	}
    534 
    535 	if (*arg == '\0' || strcmp(arg, "list") == 0) {
    536 		print_gop_modes();
    537 		return;
    538 	}
    539 
    540 	if (strchr(arg, 'x') != NULL) {
    541 		modenum = efi_find_gop_mode(arg);
    542 		if (modenum == -1) {
    543 			printf("mode %s not supported by firmware\n", arg);
    544 			return;
    545 		}
    546 	} else {
    547 		modenum = strtoul(arg, NULL, 0);
    548 	}
    549 
    550 	status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum);
    551 	if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) {
    552 		efi_gop_mode = modenum;
    553 		return;
    554 	}
    555 
    556 	printf("invalid flag, must be 'list', a display mode, "
    557 	    "or a mode number\n");
    558 }
    559 
    560 static void
    561 eficons_init_video(void)
    562 {
    563 	EFI_STATUS status;
    564 	UINTN cols, rows;
    565 	INT32 i, best, mode80x25, mode100x31;
    566 
    567 	/*
    568 	 * Setup text mode
    569 	 */
    570 	uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE);
    571 
    572 	mode80x25 = mode100x31 = -1;
    573 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    574 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    575 		    ST->ConOut, i, &cols, &rows);
    576 		if (EFI_ERROR(status))
    577 			continue;
    578 
    579 		if (mode80x25 < 0 && cols == 80 && rows == 25)
    580 			mode80x25 = i;
    581 		else if (mode100x31 < 0 && cols == 100 && rows == 31)
    582 			mode100x31 = i;
    583 	}
    584 	best = mode100x31 >= 0 ? mode100x31 : mode80x25 >= 0 ? mode80x25 : -1;
    585 	if (best >= 0)
    586 		uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best);
    587 	uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
    588 	uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
    589 
    590 	LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop);
    591 }
    592 
    593 /*
    594  * for Apple EFI
    595  */
    596 #define	CONSOLE_CONTROL_PROTOCOL \
    597 	{0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}}
    598 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL;
    599 
    600 struct _EFI_CONSOLE_CONTROL_INTERFACE;
    601 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE;
    602 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE;
    603 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) (
    604 	IN EFI_CONSOLE_CONTROL_INTERFACE *This,
    605 	IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
    606 );
    607 struct _EFI_CONSOLE_CONTROL_INTERFACE {
    608 	VOID *GetMode;
    609 	EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
    610 	VOID *LockStdIn;
    611 };
    612 
    613 static void
    614 efi_switch_video_to_text_mode(void)
    615 {
    616 	EFI_STATUS status;
    617 	EFI_CONSOLE_CONTROL_INTERFACE *cci;
    618 
    619 	/* Set up the console, so printf works. */
    620 	status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci);
    621 	if (!EFI_ERROR(status)) {
    622 		uefi_call_wrapper(cci->SetMode, 2, cci,
    623 		    EfiConsoleControlScreenText);
    624 	}
    625 }
    626