Home | History | Annotate | Line # | Download | only in efiboot
eficons.c revision 1.1
      1 /*	$NetBSD: eficons.c,v 1.1 2017/01/24 11:09:14 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 	UINT64 res, bestres = 0;
    213 	UINTN sz;
    214 	UINT32 i;
    215 	INT32 bestmode = -1;
    216 
    217 	if (efi_gop == NULL) {
    218 		framebuffer_configure(NULL);
    219 		return;
    220 	}
    221 
    222 	if (efi_gop_mode >= 0) {
    223 		bestmode = efi_gop_mode;
    224 	} else {
    225 		/* XXX EDID? EFI_EDID_DISCOVERD_PROTOCOL */
    226 		for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    227 			status = uefi_call_wrapper(efi_gop->QueryMode, 4,
    228 			    efi_gop, i, &sz, &info);
    229 			if (EFI_ERROR(status))
    230 				continue;
    231 
    232 			res = (UINT64)info->HorizontalResolution *
    233 			    (UINT64)info->VerticalResolution *
    234 			    (UINT64)getdepth(info);
    235 			if (res > bestres) {
    236 				bestmode = i;
    237 				bestres = res;
    238 			}
    239 		}
    240 	}
    241 	if (bestmode >= 0) {
    242 		status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop,
    243 		    bestmode);
    244 		if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode)
    245 			Print(L"GOP setmode failed: %r\n", status);
    246 	}
    247 
    248 	info = efi_gop->Mode->Info;
    249 	memset(&fb, 0, sizeof(fb));
    250 	fb.physaddr = efi_gop->Mode->FrameBufferBase;
    251 	fb.flags = 0;
    252 	fb.width = info->HorizontalResolution;
    253 	fb.height = info->VerticalResolution;
    254 	fb.depth = getdepth(info);
    255 	fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8);
    256 	fb.vbemode = 0;	/* XXX */
    257 
    258 	switch (info->PixelFormat) {
    259 	case PixelBlueGreenRedReserved8BitPerColor:
    260 		fb.rnum = 8;
    261 		fb.gnum = 8;
    262 		fb.bnum = 8;
    263 		fb.rpos = 16;
    264 		fb.gpos = 8;
    265 		fb.bpos = 0;
    266 		break;
    267 
    268 	case PixelRedGreenBlueReserved8BitPerColor:
    269 		fb.rnum = 8;
    270 		fb.gnum = 8;
    271 		fb.bnum = 8;
    272 		fb.rpos = 0;
    273 		fb.gpos = 8;
    274 		fb.bpos = 16;
    275 		break;
    276 
    277 	case PixelBitMask:
    278 		setpixelformat(info->PixelInformation.RedMask,
    279 		    &fb.rnum, &fb.rpos);
    280 		setpixelformat(info->PixelInformation.GreenMask,
    281 		    &fb.gnum, &fb.gpos);
    282 		setpixelformat(info->PixelInformation.BlueMask,
    283 		    &fb.bnum, &fb.bpos);
    284 		break;
    285 
    286 	case PixelBltOnly:
    287 	case PixelFormatMax:
    288 		Panic(L"Error: invalid pixel format (%d)", info->PixelFormat);
    289 		break;
    290 	}
    291 
    292 	framebuffer_configure(&fb);
    293 }
    294 
    295 int
    296 vbe_commit(void)
    297 {
    298 
    299 	bi_framebuffer();
    300 	return 0;
    301 }
    302 
    303 static void
    304 print_text_modes(void)
    305 {
    306 	EFI_STATUS status;
    307 	UINTN cols, rows;
    308 	INT32 i, curmode;
    309 
    310 	curmode = ST->ConOut->Mode->Mode;
    311 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    312 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    313 		    ST->ConOut, i, &cols, &rows);
    314 		if (EFI_ERROR(status))
    315 			continue;
    316 		Print(L"%c%d: %dx%d\n", i == curmode ? '*' : ' ',
    317 		    i, cols, rows);
    318 	}
    319 }
    320 
    321 static int
    322 efi_find_text_mode(char *arg)
    323 {
    324 	EFI_STATUS status;
    325 	UINTN cols, rows;
    326 	INT32 i;
    327 	char mode[32];
    328 
    329 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    330 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    331 		    ST->ConOut, i, &cols, &rows);
    332 		if (EFI_ERROR(status))
    333 			continue;
    334 		snprintf(mode, sizeof(mode), "%lux%lu", (long)cols, (long)rows);
    335 		if (strcmp(arg, mode) == 0)
    336 			return i;
    337 	}
    338 	return -1;
    339 }
    340 
    341 void
    342 command_text(char *arg)
    343 {
    344 	EFI_STATUS status;
    345 	INT32 modenum;
    346 
    347 	if (*arg == '\0' || strcmp(arg, "list") == 0) {
    348 		print_text_modes();
    349 		return;
    350 	}
    351 
    352 	if (strchr(arg, 'x') != NULL) {
    353 		modenum = efi_find_text_mode(arg);
    354 		if (modenum == -1) {
    355 			printf("mode %s not supported by firmware\n", arg);
    356 			return;
    357 		}
    358 	} else {
    359 		modenum = strtoul(arg, NULL, 0);
    360 	}
    361 
    362 	status = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, modenum);
    363 	if (!EFI_ERROR(status))
    364 		return;
    365 
    366 	printf("invalid flag, must be 'list', a display mode, "
    367 	    "or a mode number\n");
    368 }
    369 
    370 static int
    371 print_gop_modes(void)
    372 {
    373 	EFI_STATUS status;
    374 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    375 	UINTN sz;
    376 	UINT32 i;
    377 	uint8_t depth;
    378 
    379 	if (efi_gop == NULL)
    380 		return 1;
    381 
    382 	for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    383 		status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
    384 		    &sz, &info);
    385 		if (EFI_ERROR(status) && status == EFI_NOT_STARTED) {
    386 			status = uefi_call_wrapper(efi_gop->SetMode, 2,
    387 			    efi_gop, efi_gop->Mode->Mode);
    388 			status = uefi_call_wrapper(efi_gop->QueryMode, 4,
    389 			    efi_gop, i, &sz, &info);
    390 		}
    391 		if (EFI_ERROR(status))
    392 			continue;
    393 
    394 		Print(L"%c%d: %dx%d ",
    395 		    memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ?
    396 		      '*' : ' ',
    397 		      i, info->HorizontalResolution, info->VerticalResolution);
    398 		switch (info->PixelFormat) {
    399 		case PixelRedGreenBlueReserved8BitPerColor:
    400 			Print(L"RGBR");
    401 			break;
    402 		case PixelBlueGreenRedReserved8BitPerColor:
    403 			Print(L"BGRR");
    404 			break;
    405 		case PixelBitMask:
    406 			Print(L"R:%08x G:%08x B:%08x X:%08x",
    407 			    info->PixelInformation.RedMask,
    408 			    info->PixelInformation.GreenMask,
    409 			    info->PixelInformation.BlueMask,
    410 			    info->PixelInformation.ReservedMask);
    411 			break;
    412 		case PixelBltOnly:
    413 			Print(L"(blt only)");
    414 			break;
    415 		default:
    416 			Print(L"(Invalid pixel format)");
    417 			break;
    418 		}
    419 		Print(L" pitch %d", info->PixelsPerScanLine);
    420 		depth = getdepth(info);
    421 		if (depth > 0)
    422 			Print(L" bpp %d", depth);
    423 		Print(L"\n");
    424 	}
    425 
    426 	return 0;
    427 }
    428 
    429 static int
    430 efi_find_gop_mode(char *arg)
    431 {
    432 	EFI_STATUS status;
    433 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
    434 	UINTN sz;
    435 	UINT32 i;
    436 	char mode[32];
    437 	uint8_t depth;
    438 
    439 	for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
    440 		status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
    441 		    &sz, &info);
    442 		if (EFI_ERROR(status))
    443 			continue;
    444 
    445 		depth = getdepth(info);
    446 		if (depth == 0)
    447 			continue;
    448 
    449 		snprintf(mode, sizeof(mode), "%lux%lux%u",
    450 		    (long)info->HorizontalResolution,
    451 		    (long)info->HorizontalResolution,
    452 		    depth);
    453 		if (strcmp(arg, mode) == 0)
    454 			return i;
    455 	}
    456 	return -1;
    457 }
    458 
    459 void
    460 command_gop(char *arg)
    461 {
    462 	EFI_STATUS status;
    463 	INT32 modenum;
    464 
    465 	if (efi_gop == NULL) {
    466 		printf("GOP not supported by firmware\n");
    467 		return;
    468 	}
    469 
    470 	if (*arg == '\0' || strcmp(arg, "list") == 0) {
    471 		print_gop_modes();
    472 		return;
    473 	}
    474 
    475 	if (strchr(arg, 'x') != NULL) {
    476 		modenum = efi_find_gop_mode(arg);
    477 		if (modenum == -1) {
    478 			printf("mode %s not supported by firmware\n", arg);
    479 			return;
    480 		}
    481 	} else {
    482 		modenum = strtoul(arg, NULL, 0);
    483 	}
    484 
    485 	status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum);
    486 	if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) {
    487 		efi_gop_mode = modenum;
    488 		return;
    489 	}
    490 
    491 	printf("invalid flag, must be 'list', a display mode, "
    492 	    "or a mode number\n");
    493 }
    494 
    495 static void
    496 eficons_init_video(void)
    497 {
    498 	EFI_STATUS status;
    499 	UINTN cols, rows, dim = 0;
    500 	INT32 i, best = -1;
    501 
    502 	/*
    503 	 * Setup text mode
    504 	 */
    505 	uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE);
    506 
    507 	for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
    508 		status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
    509 		    ST->ConOut, i, &cols, &rows);
    510 		if (EFI_ERROR(status))
    511 			continue;
    512 		if (dim < cols * rows) {
    513 			dim = cols * rows;
    514 			best = i;
    515 		}
    516 	}
    517 	if (best >= 0)
    518 		uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best);
    519 	uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
    520 	uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
    521 
    522 	LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop);
    523 }
    524 
    525 /*
    526  * for Apple EFI
    527  */
    528 #define	CONSOLE_CONTROL_PROTOCOL \
    529 	{0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}}
    530 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL;
    531 
    532 struct _EFI_CONSOLE_CONTROL_INTERFACE;
    533 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE;
    534 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE;
    535 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) (
    536 	IN EFI_CONSOLE_CONTROL_INTERFACE *This,
    537 	IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
    538 );
    539 struct _EFI_CONSOLE_CONTROL_INTERFACE {
    540 	VOID *GetMode;
    541 	EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
    542 	VOID *LockStdIn;
    543 };
    544 
    545 static void
    546 efi_switch_video_to_text_mode(void)
    547 {
    548 	EFI_STATUS status;
    549 	EFI_CONSOLE_CONTROL_INTERFACE *cci;
    550 
    551 	/* Set up the console, so printf works. */
    552 	status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci);
    553 	if (!EFI_ERROR(status)) {
    554 		uefi_call_wrapper(cci->SetMode, 2, cci,
    555 		    EfiConsoleControlScreenText);
    556 	}
    557 }
    558