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