xref: /freebsd/stand/efi/loader/framebuffer.c (revision 43b8edb320519c9887a5d953c4cf8a91f0ca8d14)
1 /*-
2  * Copyright (c) 2013 The FreeBSD Foundation
3  *
4  * This software was developed by Benno Rice under sponsorship from
5  * the FreeBSD Foundation.
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <bootstrap.h>
29 #include <sys/endian.h>
30 #include <sys/param.h>
31 #include <stand.h>
32 
33 #include <efi.h>
34 #include <efilib.h>
35 #include <efipciio.h>
36 #include <Protocol/EdidActive.h>
37 #include <Protocol/EdidDiscovered.h>
38 #include <Protocol/GraphicsOutput.h>
39 #include <Protocol/UgaDraw.h>
40 #include <machine/metadata.h>
41 
42 #include "bootstrap.h"
43 #include "framebuffer.h"
44 
45 /* XXX This may be obsolete -- edk2 doesn't define it anywhere */
46 #define EFI_CONSOLE_OUT_DEVICE_GUID    \
47 { 0xd3b36f2c, 0xd551, 0x11d4, {0x9a, 0x46, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }
48 
49 static EFI_GUID conout_guid = EFI_CONSOLE_OUT_DEVICE_GUID;
50 EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
51 static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
52 static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID;
53 static EFI_GUID active_edid_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID;
54 static EFI_GUID discovered_edid_guid = EFI_EDID_DISCOVERED_PROTOCOL_GUID;
55 static EFI_HANDLE gop_handle;
56 
57 /* Cached EDID. */
58 struct vesa_edid_info *edid_info = NULL;
59 
60 static EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
61 static EFI_UGA_DRAW_PROTOCOL *uga;
62 
63 static struct named_resolution {
64 	const char *name;
65 	const char *alias;
66 	unsigned int width;
67 	unsigned int height;
68 } resolutions[] = {
69 	{
70 		.name = "480p",
71 		.width = 640,
72 		.height = 480,
73 	},
74 	{
75 		.name = "720p",
76 		.width = 1280,
77 		.height = 720,
78 	},
79 	{
80 		.name = "1080p",
81 		.width = 1920,
82 		.height = 1080,
83 	},
84 	{
85 		.name = "1440p",
86 		.width = 2560,
87 		.height = 1440,
88 	},
89 	{
90 		.name = "2160p",
91 		.alias = "4k",
92 		.width = 3840,
93 		.height = 2160,
94 	},
95 	{
96 		.name = "5k",
97 		.width = 5120,
98 		.height = 2880,
99 	}
100 };
101 
102 static u_int
efifb_color_depth(struct efi_fb * efifb)103 efifb_color_depth(struct efi_fb *efifb)
104 {
105 	uint32_t mask;
106 	u_int depth;
107 
108 	mask = efifb->fb_mask_red | efifb->fb_mask_green |
109 	    efifb->fb_mask_blue | efifb->fb_mask_reserved;
110 	if (mask == 0)
111 		return (0);
112 	for (depth = 1; mask != 1; depth++)
113 		mask >>= 1;
114 	return (depth);
115 }
116 
117 static int
efifb_mask_from_pixfmt(struct efi_fb * efifb,EFI_GRAPHICS_PIXEL_FORMAT pixfmt,EFI_PIXEL_BITMASK * pixinfo)118 efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt,
119     EFI_PIXEL_BITMASK *pixinfo)
120 {
121 	int result;
122 
123 	result = 0;
124 	switch (pixfmt) {
125 	case PixelRedGreenBlueReserved8BitPerColor:
126 	case PixelBltOnly:
127 		efifb->fb_mask_red = 0x000000ff;
128 		efifb->fb_mask_green = 0x0000ff00;
129 		efifb->fb_mask_blue = 0x00ff0000;
130 		efifb->fb_mask_reserved = 0xff000000;
131 		break;
132 	case PixelBlueGreenRedReserved8BitPerColor:
133 		efifb->fb_mask_red = 0x00ff0000;
134 		efifb->fb_mask_green = 0x0000ff00;
135 		efifb->fb_mask_blue = 0x000000ff;
136 		efifb->fb_mask_reserved = 0xff000000;
137 		break;
138 	case PixelBitMask:
139 		efifb->fb_mask_red = pixinfo->RedMask;
140 		efifb->fb_mask_green = pixinfo->GreenMask;
141 		efifb->fb_mask_blue = pixinfo->BlueMask;
142 		efifb->fb_mask_reserved = pixinfo->ReservedMask;
143 		break;
144 	default:
145 		result = 1;
146 		break;
147 	}
148 	return (result);
149 }
150 
151 static int
efifb_from_gop(struct efi_fb * efifb,EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE * mode,EFI_GRAPHICS_OUTPUT_MODE_INFORMATION * info)152 efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode,
153     EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
154 {
155 	int result;
156 
157 	/*
158 	 * The Asus EEEPC 1025C, and possibly others,
159 	 * require the address to be masked.
160 	 */
161 	efifb->fb_addr =
162 #ifdef __i386__
163 	    mode->FrameBufferBase & 0xffffffff;
164 #else
165 	    mode->FrameBufferBase;
166 #endif
167 	efifb->fb_size = mode->FrameBufferSize;
168 	efifb->fb_height = info->VerticalResolution;
169 	efifb->fb_width = info->HorizontalResolution;
170 	efifb->fb_stride = info->PixelsPerScanLine;
171 	result = efifb_mask_from_pixfmt(efifb, info->PixelFormat,
172 	    &info->PixelInformation);
173 	return (result);
174 }
175 
176 static ssize_t
efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL * uga,u_int line,EFI_PCI_IO_PROTOCOL * pciio,uint64_t addr,uint64_t size)177 efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line,
178     EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size)
179 {
180 	EFI_UGA_PIXEL pix0, pix1;
181 	uint8_t *data1, *data2;
182 	size_t count, maxcount = 1024;
183 	ssize_t ofs;
184 	EFI_STATUS status;
185 	u_int idx;
186 
187 	status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer,
188 	    0, line, 0, 0, 1, 1, 0);
189 	if (EFI_ERROR(status)) {
190 		printf("UGA BLT operation failed (video->buffer)");
191 		return (-1);
192 	}
193 	pix1.Red = ~pix0.Red;
194 	pix1.Green = ~pix0.Green;
195 	pix1.Blue = ~pix0.Blue;
196 	pix1.Reserved = 0;
197 
198 	data1 = calloc(maxcount, 2);
199 	if (data1 == NULL) {
200 		printf("Unable to allocate memory");
201 		return (-1);
202 	}
203 	data2 = data1 + maxcount;
204 
205 	ofs = 0;
206 	while (size > 0) {
207 		count = min(size, maxcount);
208 
209 		status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
210 		    EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
211 		    data1);
212 		if (EFI_ERROR(status)) {
213 			printf("Error reading frame buffer (before)");
214 			goto fail;
215 		}
216 		status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo,
217 		    0, 0, 0, line, 1, 1, 0);
218 		if (EFI_ERROR(status)) {
219 			printf("UGA BLT operation failed (modify)");
220 			goto fail;
221 		}
222 		status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
223 		    EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
224 		    data2);
225 		if (EFI_ERROR(status)) {
226 			printf("Error reading frame buffer (after)");
227 			goto fail;
228 		}
229 		status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo,
230 		    0, 0, 0, line, 1, 1, 0);
231 		if (EFI_ERROR(status)) {
232 			printf("UGA BLT operation failed (restore)");
233 			goto fail;
234 		}
235 		for (idx = 0; idx < count; idx++) {
236 			if (data1[idx] != data2[idx]) {
237 				free(data1);
238 				return (ofs + (idx & ~3));
239 			}
240 		}
241 		ofs += count;
242 		size -= count;
243 	}
244 	printf("No change detected in frame buffer");
245 
246  fail:
247 	printf(" -- error %lu\n", DECODE_ERROR(status));
248 	free(data1);
249 	return (-1);
250 }
251 
252 static EFI_PCI_IO_PROTOCOL *
efifb_uga_get_pciio(void)253 efifb_uga_get_pciio(void)
254 {
255 	EFI_PCI_IO_PROTOCOL *pciio;
256 	EFI_HANDLE *buf, *hp;
257 	EFI_STATUS status;
258 	UINTN bufsz;
259 
260 	/* Get all handles that support the UGA protocol. */
261 	bufsz = 0;
262 	status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL);
263 	if (status != EFI_BUFFER_TOO_SMALL)
264 		return (NULL);
265 	buf = malloc(bufsz);
266 	status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf);
267 	if (status != EFI_SUCCESS) {
268 		free(buf);
269 		return (NULL);
270 	}
271 	bufsz /= sizeof(EFI_HANDLE);
272 
273 	/* Get the PCI I/O interface of the first handle that supports it. */
274 	pciio = NULL;
275 	for (hp = buf; hp < buf + bufsz; hp++) {
276 		status = OpenProtocolByHandle(*hp, &pciio_guid,
277 		    (void **)&pciio);
278 		if (status == EFI_SUCCESS) {
279 			free(buf);
280 			return (pciio);
281 		}
282 	}
283 	free(buf);
284 	return (NULL);
285 }
286 
287 static EFI_STATUS
efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL * pciio,uint64_t * addrp,uint64_t * sizep)288 efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp,
289     uint64_t *sizep)
290 {
291 	uint8_t *resattr;
292 	uint64_t addr, size;
293 	EFI_STATUS status;
294 	u_int bar;
295 
296 	if (pciio == NULL)
297 		return (EFI_DEVICE_ERROR);
298 
299 	/* Attempt to get the frame buffer address (imprecise). */
300 	*addrp = 0;
301 	*sizep = 0;
302 	for (bar = 0; bar < 6; bar++) {
303 		status = pciio->GetBarAttributes(pciio, bar, NULL,
304 		    (void **)&resattr);
305 		if (status != EFI_SUCCESS)
306 			continue;
307 		/* XXX magic offsets and constants. */
308 		if (resattr[0] == 0x87 && resattr[3] == 0) {
309 			/* 32-bit address space descriptor (MEMIO) */
310 			addr = le32dec(resattr + 10);
311 			size = le32dec(resattr + 22);
312 		} else if (resattr[0] == 0x8a && resattr[3] == 0) {
313 			/* 64-bit address space descriptor (MEMIO) */
314 			addr = le64dec(resattr + 14);
315 			size = le64dec(resattr + 38);
316 		} else {
317 			addr = 0;
318 			size = 0;
319 		}
320 		BS->FreePool(resattr);
321 		if (addr == 0 || size == 0)
322 			continue;
323 
324 		/* We assume the largest BAR is the frame buffer. */
325 		if (size > *sizep) {
326 			*addrp = addr;
327 			*sizep = size;
328 		}
329 	}
330 	return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0);
331 }
332 
333 static int
efifb_from_uga(struct efi_fb * efifb)334 efifb_from_uga(struct efi_fb *efifb)
335 {
336 	EFI_PCI_IO_PROTOCOL *pciio;
337 	char *ev, *p;
338 	EFI_STATUS status;
339 	ssize_t offset;
340 	uint64_t fbaddr;
341 	uint32_t horiz, vert, stride;
342 	uint32_t np, depth, refresh;
343 
344 	status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh);
345 	if (EFI_ERROR(status))
346 		return (1);
347 	efifb->fb_height = vert;
348 	efifb->fb_width = horiz;
349 	/* Paranoia... */
350 	if (efifb->fb_height == 0 || efifb->fb_width == 0)
351 		return (1);
352 
353 	/* The color masks are fixed AFAICT. */
354 	efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor,
355 	    NULL);
356 
357 	/* pciio can be NULL on return! */
358 	pciio = efifb_uga_get_pciio();
359 
360 	/* Try to find the frame buffer. */
361 	status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr,
362 	    &efifb->fb_size);
363 	if (EFI_ERROR(status)) {
364 		efifb->fb_addr = 0;
365 		efifb->fb_size = 0;
366 	}
367 
368 	/*
369 	 * There's no reliable way to detect the frame buffer or the
370 	 * offset within the frame buffer of the visible region, nor
371 	 * the stride. Our only option is to look at the system and
372 	 * fill in the blanks based on that. Luckily, UGA was mostly
373 	 * only used on Apple hardware.
374 	 */
375 	offset = -1;
376 	ev = getenv("smbios.system.maker");
377 	if (ev != NULL && !strcmp(ev, "Apple Inc.")) {
378 		ev = getenv("smbios.system.product");
379 		if (ev != NULL && !strcmp(ev, "iMac7,1")) {
380 			/* These are the expected values we should have. */
381 			horiz = 1680;
382 			vert = 1050;
383 			fbaddr = 0xc0000000;
384 			/* These are the missing bits. */
385 			offset = 0x10000;
386 			stride = 1728;
387 		} else if (ev != NULL && !strcmp(ev, "MacBook3,1")) {
388 			/* These are the expected values we should have. */
389 			horiz = 1280;
390 			vert = 800;
391 			fbaddr = 0xc0000000;
392 			/* These are the missing bits. */
393 			offset = 0x0;
394 			stride = 2048;
395 		}
396 	}
397 
398 	/*
399 	 * If this is hardware we know, make sure that it looks familiar
400 	 * before we accept our hardcoded values.
401 	 */
402 	if (offset >= 0 && efifb->fb_width == horiz &&
403 	    efifb->fb_height == vert && efifb->fb_addr == fbaddr) {
404 		efifb->fb_addr += offset;
405 		efifb->fb_size -= offset;
406 		efifb->fb_stride = stride;
407 		return (0);
408 	} else if (offset >= 0) {
409 		printf("Hardware make/model known, but graphics not "
410 		    "as expected.\n");
411 		printf("Console may not work!\n");
412 	}
413 
414 	/*
415 	 * The stride is equal or larger to the width. Often it's the
416 	 * next larger power of two. We'll start with that...
417 	 */
418 	efifb->fb_stride = efifb->fb_width;
419 	do {
420 		np = efifb->fb_stride & (efifb->fb_stride - 1);
421 		if (np) {
422 			efifb->fb_stride |= (np - 1);
423 			efifb->fb_stride++;
424 		}
425 	} while (np);
426 
427 	ev = getenv("hw.efifb.address");
428 	if (ev == NULL) {
429 		if (efifb->fb_addr == 0) {
430 			printf("Please set hw.efifb.address and "
431 			    "hw.efifb.stride.\n");
432 			return (1);
433 		}
434 
435 		/*
436 		 * The visible part of the frame buffer may not start at
437 		 * offset 0, so try to detect it. Note that we may not
438 		 * always be able to read from the frame buffer, which
439 		 * means that we may not be able to detect anything. In
440 		 * that case, we would take a long time scanning for a
441 		 * pixel change in the frame buffer, which would have it
442 		 * appear that we're hanging, so we limit the scan to
443 		 * 1/256th of the frame buffer. This number is mostly
444 		 * based on PR 202730 and the fact that on a MacBoook,
445 		 * where we can't read from the frame buffer the offset
446 		 * of the visible region is 0. In short: we want to scan
447 		 * enough to handle all adapters that have an offset
448 		 * larger than 0 and we want to scan as little as we can
449 		 * to not appear to hang when we can't read from the
450 		 * frame buffer.
451 		 */
452 		offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr,
453 		    efifb->fb_size >> 8);
454 		if (offset == -1) {
455 			printf("Unable to reliably detect frame buffer.\n");
456 		} else if (offset > 0) {
457 			efifb->fb_addr += offset;
458 			efifb->fb_size -= offset;
459 		}
460 	} else {
461 		offset = 0;
462 		efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
463 		efifb->fb_addr = strtoul(ev, &p, 0);
464 		if (*p != '\0')
465 			return (1);
466 	}
467 
468 	ev = getenv("hw.efifb.stride");
469 	if (ev == NULL) {
470 		if (pciio != NULL && offset != -1) {
471 			/* Determine the stride. */
472 			offset = efifb_uga_find_pixel(uga, 1, pciio,
473 			    efifb->fb_addr, horiz * 8);
474 			if (offset != -1)
475 				efifb->fb_stride = offset >> 2;
476 		} else {
477 			printf("Unable to reliably detect the stride.\n");
478 		}
479 	} else {
480 		efifb->fb_stride = strtoul(ev, &p, 0);
481 		if (*p != '\0')
482 			return (1);
483 	}
484 
485 	/*
486 	 * We finalized on the stride, so recalculate the size of the
487 	 * frame buffer.
488 	 */
489 	efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
490 	return (0);
491 }
492 
493 /*
494  * Fetch EDID info. Caller must free the buffer.
495  */
496 static struct vesa_edid_info *
efifb_gop_get_edid(EFI_HANDLE h)497 efifb_gop_get_edid(EFI_HANDLE h)
498 {
499 	const uint8_t magic[] = EDID_MAGIC;
500 	EFI_EDID_ACTIVE_PROTOCOL *edid;
501 	struct vesa_edid_info *edid_infop;
502 	EFI_GUID *guid;
503 	EFI_STATUS status;
504 	size_t size;
505 
506 	guid = &active_edid_guid;
507 	status = BS->OpenProtocol(h, guid, (void **)&edid, IH, NULL,
508 	    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
509 	if (status != EFI_SUCCESS ||
510 	    edid->SizeOfEdid == 0) {
511 		guid = &discovered_edid_guid;
512 		status = BS->OpenProtocol(h, guid, (void **)&edid, IH, NULL,
513 		    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
514 		if (status != EFI_SUCCESS ||
515 		    edid->SizeOfEdid == 0)
516 			return (NULL);
517 	}
518 
519 	size = MAX(sizeof(*edid_infop), edid->SizeOfEdid);
520 
521 	edid_infop = calloc(1, size);
522 	if (edid_infop == NULL)
523 		return (NULL);
524 
525 	memcpy(edid_infop, edid->Edid, edid->SizeOfEdid);
526 
527 	/* Validate EDID */
528 	if (memcmp(edid_infop, magic, sizeof (magic)) != 0)
529 		goto error;
530 
531 	if (edid_infop->header.version != 1)
532 		goto error;
533 
534 	return (edid_infop);
535 error:
536 	free(edid_infop);
537 	return (NULL);
538 }
539 
540 static bool
efifb_get_edid(edid_res_list_t * res)541 efifb_get_edid(edid_res_list_t *res)
542 {
543 	bool rv = false;
544 
545 	if (edid_info == NULL)
546 		edid_info = efifb_gop_get_edid(gop_handle);
547 
548 	if (edid_info != NULL)
549 		rv = gfx_get_edid_resolution(edid_info, res);
550 
551 	return (rv);
552 }
553 
554 bool
efi_has_gop(void)555 efi_has_gop(void)
556 {
557 	EFI_STATUS status;
558 	EFI_HANDLE *hlist;
559 	UINTN hsize;
560 
561 	hsize = 0;
562 	hlist = NULL;
563 	status = BS->LocateHandle(ByProtocol, &gop_guid, NULL, &hsize, hlist);
564 
565 	return (status == EFI_BUFFER_TOO_SMALL);
566 }
567 
568 
569 int
efi_find_framebuffer(teken_gfx_t * gfx_state)570 efi_find_framebuffer(teken_gfx_t *gfx_state)
571 {
572 	EFI_PHYSICAL_ADDRESS ptr;
573 	EFI_HANDLE *hlist;
574 	UINTN nhandles, i, hsize;
575 	struct efi_fb efifb;
576 	EFI_STATUS status;
577 	int rv;
578 
579 	gfx_state->tg_fb_type = FB_TEXT;
580 
581 	hsize = 0;
582 	hlist = NULL;
583 	status = BS->LocateHandle(ByProtocol, &gop_guid, NULL, &hsize, hlist);
584 	if (status == EFI_BUFFER_TOO_SMALL) {
585 		hlist = malloc(hsize);
586 		if (hlist == NULL)
587 			return (ENOMEM);
588 		status = BS->LocateHandle(ByProtocol, &gop_guid, NULL, &hsize,
589 		    hlist);
590 		if (EFI_ERROR(status))
591 			free(hlist);
592 	}
593 	if (EFI_ERROR(status))
594 		return (efi_status_to_errno(status));
595 
596 	nhandles = hsize / sizeof(*hlist);
597 
598 	/*
599 	 * Search for ConOut protocol, if not found, use first handle.
600 	 */
601 	gop_handle = NULL;
602 	for (i = 0; i < nhandles; i++) {
603 		EFI_GRAPHICS_OUTPUT_PROTOCOL *tgop;
604 		void *dummy;
605 
606 		status = OpenProtocolByHandle(hlist[i], &gop_guid, (void **)&tgop);
607 		if (status != EFI_SUCCESS)
608 			continue;
609 
610 		if (tgop->Mode->Info->PixelFormat == PixelBltOnly ||
611 		    tgop->Mode->Info->PixelFormat >= PixelFormatMax)
612 			continue;
613 
614 		status = OpenProtocolByHandle(hlist[i], &conout_guid, &dummy);
615 		if (status == EFI_SUCCESS) {
616 			gop_handle = hlist[i];
617 			gop = tgop;
618 			break;
619 		} else if (gop_handle == NULL) {
620 			gop_handle = hlist[i];
621 			gop = tgop;
622 		}
623 	}
624 
625 	free(hlist);
626 
627 	if (gop_handle != NULL) {
628 		gfx_state->tg_fb_type = FB_GOP;
629 		gfx_state->tg_private = gop;
630 		if (edid_info == NULL)
631 			edid_info = efifb_gop_get_edid(gop_handle);
632 	} else {
633 		status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
634 		if (status == EFI_SUCCESS) {
635 			gfx_state->tg_fb_type = FB_UGA;
636 			gfx_state->tg_private = uga;
637 		} else {
638 			return (efi_status_to_errno(status));
639 		}
640 	}
641 
642 	switch (gfx_state->tg_fb_type) {
643 	case FB_GOP:
644 		rv = efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
645 		break;
646 
647 	case FB_UGA:
648 		rv = efifb_from_uga(&efifb);
649 		break;
650 
651 	default:
652 		return (EINVAL);
653 	}
654 
655 	if (rv != 0)
656 		return (rv);
657 
658 	gfx_state->tg_fb.fb_addr = efifb.fb_addr;
659 	gfx_state->tg_fb.fb_size = efifb.fb_size;
660 	gfx_state->tg_fb.fb_height = efifb.fb_height;
661 	gfx_state->tg_fb.fb_width = efifb.fb_width;
662 	gfx_state->tg_fb.fb_stride = efifb.fb_stride;
663 	gfx_state->tg_fb.fb_mask_red = efifb.fb_mask_red;
664 	gfx_state->tg_fb.fb_mask_green = efifb.fb_mask_green;
665 	gfx_state->tg_fb.fb_mask_blue = efifb.fb_mask_blue;
666 	gfx_state->tg_fb.fb_mask_reserved = efifb.fb_mask_reserved;
667 
668 	gfx_state->tg_fb.fb_bpp = fls(efifb.fb_mask_red | efifb.fb_mask_green |
669 	    efifb.fb_mask_blue | efifb.fb_mask_reserved);
670 
671 	if (gfx_state->tg_shadow_fb != NULL)
672 		BS->FreePages((uintptr_t)gfx_state->tg_shadow_fb,
673 		    gfx_state->tg_shadow_sz);
674 	gfx_state->tg_shadow_sz =
675 	    EFI_SIZE_TO_PAGES(efifb.fb_height * efifb.fb_width *
676 	    sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
677 	status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
678 	    gfx_state->tg_shadow_sz, &ptr);
679 	gfx_state->tg_shadow_fb = status == EFI_SUCCESS ?
680 	    (uint32_t *)(uintptr_t)ptr : NULL;
681 
682 	return (0);
683 }
684 
685 static void
print_efifb(int mode,struct efi_fb * efifb,int verbose)686 print_efifb(int mode, struct efi_fb *efifb, int verbose)
687 {
688 	u_int depth;
689 
690 	if (mode >= 0)
691 		printf("mode %d: ", mode);
692 	depth = efifb_color_depth(efifb);
693 	printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height,
694 	    depth, efifb->fb_stride);
695 	if (verbose) {
696 		printf("\n    frame buffer: address=%jx, size=%jx",
697 		    (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size);
698 		printf("\n    color mask: R=%08x, G=%08x, B=%08x\n",
699 		    efifb->fb_mask_red, efifb->fb_mask_green,
700 		    efifb->fb_mask_blue);
701 	}
702 }
703 
704 static bool
efi_resolution_compare(struct named_resolution * res,const char * cmp)705 efi_resolution_compare(struct named_resolution *res, const char *cmp)
706 {
707 
708 	if (strcasecmp(res->name, cmp) == 0)
709 		return (true);
710 	if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0)
711 		return (true);
712 	return (false);
713 }
714 
715 
716 static void
efi_get_max_resolution(int * width,int * height)717 efi_get_max_resolution(int *width, int *height)
718 {
719 	struct named_resolution *res;
720 	char *maxres;
721 	char *height_start, *width_start;
722 	int idx;
723 
724 	*width = *height = 0;
725 	maxres = getenv("efi_max_resolution");
726 	/* No max_resolution set? Bail out; choose highest resolution */
727 	if (maxres == NULL)
728 		return;
729 	/* See if it matches one of our known resolutions */
730 	for (idx = 0; idx < nitems(resolutions); ++idx) {
731 		res = &resolutions[idx];
732 		if (efi_resolution_compare(res, maxres)) {
733 			*width = res->width;
734 			*height = res->height;
735 			return;
736 		}
737 	}
738 	/* Not a known resolution, try to parse it; make a copy we can modify */
739 	maxres = strdup(maxres);
740 	if (maxres == NULL)
741 		return;
742 	height_start = strchr(maxres, 'x');
743 	if (height_start == NULL) {
744 		free(maxres);
745 		return;
746 	}
747 	width_start = maxres;
748 	*height_start++ = 0;
749 	/* Errors from this will effectively mean "no max" */
750 	*width = (int)strtol(width_start, NULL, 0);
751 	*height = (int)strtol(height_start, NULL, 0);
752 	free(maxres);
753 }
754 
755 static int
gop_autoresize(void)756 gop_autoresize(void)
757 {
758 	struct efi_fb efifb;
759 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
760 	EFI_STATUS status;
761 	UINTN infosz;
762 	UINT32 best_mode, currdim, maxdim, mode;
763 	int height, max_height, max_width, width;
764 
765 	best_mode = maxdim = 0;
766 	efi_get_max_resolution(&max_width, &max_height);
767 	for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
768 		status = gop->QueryMode(gop, mode, &infosz, &info);
769 		if (EFI_ERROR(status))
770 			continue;
771 		efifb_from_gop(&efifb, gop->Mode, info);
772 		width = info->HorizontalResolution;
773 		height = info->VerticalResolution;
774 		currdim = width * height;
775 		if (currdim > maxdim) {
776 			if ((max_width != 0 && width > max_width) ||
777 			    (max_height != 0 && height > max_height))
778 				continue;
779 			maxdim = currdim;
780 			best_mode = mode;
781 		}
782 	}
783 
784 	if (maxdim != 0) {
785 		status = gop->SetMode(gop, best_mode);
786 		if (EFI_ERROR(status)) {
787 			snprintf(command_errbuf, sizeof(command_errbuf),
788 			    "gop_autoresize: Unable to set mode to %u (error=%lu)",
789 			    mode, DECODE_ERROR(status));
790 			return (CMD_ERROR);
791 		}
792 		(void) cons_update_mode(true);
793 	}
794 	return (CMD_OK);
795 }
796 
797 static int
text_autoresize()798 text_autoresize()
799 {
800 	SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
801 	EFI_STATUS status;
802 	UINTN i, max_dim, best_mode, cols, rows;
803 
804 	conout = ST->ConOut;
805 	max_dim = best_mode = 0;
806 	for (i = 0; i < conout->Mode->MaxMode; i++) {
807 		status = conout->QueryMode(conout, i, &cols, &rows);
808 		if (EFI_ERROR(status))
809 			continue;
810 		if (cols * rows > max_dim) {
811 			max_dim = cols * rows;
812 			best_mode = i;
813 		}
814 	}
815 	if (max_dim > 0)
816 		conout->SetMode(conout, best_mode);
817 	(void) cons_update_mode(true);
818 	return (CMD_OK);
819 }
820 
821 static int
uga_autoresize(void)822 uga_autoresize(void)
823 {
824 
825 	return (text_autoresize());
826 }
827 
828 COMMAND_SET(efi_autoresize, "efi-autoresizecons", "EFI Auto-resize Console", command_autoresize);
829 
830 static int
command_autoresize(int argc,char * argv[])831 command_autoresize(int argc, char *argv[])
832 {
833 	char *textmode;
834 
835 	textmode = getenv("hw.vga.textmode");
836 	/* If it's set and non-zero, we'll select a console mode instead */
837 	if (textmode != NULL && strcmp(textmode, "0") != 0)
838 		return (text_autoresize());
839 
840 	if (gop != NULL)
841 		return (gop_autoresize());
842 
843 	if (uga != NULL)
844 		return (uga_autoresize());
845 
846 	snprintf(command_errbuf, sizeof(command_errbuf),
847 	    "%s: Neither Graphics Output Protocol nor Universal Graphics Adapter present",
848 	    argv[0]);
849 
850 	/*
851 	 * Default to text_autoresize if we have neither GOP or UGA.  This won't
852 	 * give us the most ideal resolution, but it will at least leave us
853 	 * functional rather than failing the boot for an objectively bad
854 	 * reason.
855 	 */
856 	return (text_autoresize());
857 }
858 
859 COMMAND_SET(gop, "gop", "graphics output protocol", command_gop);
860 
861 static int
command_gop(int argc,char * argv[])862 command_gop(int argc, char *argv[])
863 {
864 	struct efi_fb efifb;
865 	EFI_STATUS status;
866 	u_int mode;
867 	extern bool ignore_gop_blt;
868 
869 	if (gop == NULL) {
870 		snprintf(command_errbuf, sizeof(command_errbuf),
871 		    "%s: Graphics Output Protocol not present", argv[0]);
872 		return (CMD_ERROR);
873 	}
874 
875 	if (argc < 2)
876 		goto usage;
877 
878 	if (strcmp(argv[1], "set") == 0) {
879 		char *cp;
880 
881 		if (argc != 3)
882 			goto usage;
883 		mode = strtol(argv[2], &cp, 0);
884 		if (cp[0] != '\0') {
885 			sprintf(command_errbuf, "mode is an integer");
886 			return (CMD_ERROR);
887 		}
888 		status = gop->SetMode(gop, mode);
889 		if (EFI_ERROR(status)) {
890 			snprintf(command_errbuf, sizeof(command_errbuf),
891 			    "%s: Unable to set mode to %u (error=%lu)",
892 			    argv[0], mode, DECODE_ERROR(status));
893 			return (CMD_ERROR);
894 		}
895 		(void) cons_update_mode(true);
896 	} else if (strcmp(argv[1], "blt") == 0) {
897 		/*
898 		 * "blt on" does allow gop->Blt() to be used (default).
899 		 * "blt off" does block gop->Blt() to be used and use
900 		 * software rendering instead.
901 		 */
902 		if (argc != 3)
903 			goto usage;
904 		if (strcmp(argv[2], "on") == 0)
905 			ignore_gop_blt = false;
906 		else if (strcmp(argv[2], "off") == 0)
907 			ignore_gop_blt = true;
908 		else
909 			goto usage;
910 	} else if (strcmp(argv[1], "off") == 0) {
911 		/*
912 		 * Tell console to use SimpleTextOutput protocol.
913 		 * This means that we do not render the glyphs, but rely on
914 		 * UEFI firmware to draw on ConsOut device(s).
915 		 */
916 		(void) cons_update_mode(false);
917 	} else if (strcmp(argv[1], "get") == 0) {
918 		edid_res_list_t res;
919 
920 		if (argc != 2)
921 			goto usage;
922 		TAILQ_INIT(&res);
923 		efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
924 		if (efifb_get_edid(&res)) {
925 			struct resolution *rp;
926 
927 			printf("EDID");
928 			while ((rp = TAILQ_FIRST(&res)) != NULL) {
929 				printf(" %dx%d", rp->width, rp->height);
930 				TAILQ_REMOVE(&res, rp, next);
931 				free(rp);
932 			}
933 			printf("\n");
934 		} else {
935 			printf("no EDID information\n");
936 		}
937 		print_efifb(gop->Mode->Mode, &efifb, 1);
938 		printf("\n");
939 	} else if (strcmp(argv[1], "list") == 0) {
940 		EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
941 		UINTN infosz;
942 
943 		if (argc != 2)
944 			goto usage;
945 
946 		pager_open();
947 		for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
948 			status = gop->QueryMode(gop, mode, &infosz, &info);
949 			if (EFI_ERROR(status))
950 				continue;
951 			efifb_from_gop(&efifb, gop->Mode, info);
952 			print_efifb(mode, &efifb, 0);
953 			if (pager_output("\n"))
954 				break;
955 		}
956 		pager_close();
957 	}
958 	return (CMD_OK);
959 
960  usage:
961 	snprintf(command_errbuf, sizeof(command_errbuf),
962 	    "usage: %s [list | get | set <mode> | off | blt <on|off>]", argv[0]);
963 	return (CMD_ERROR);
964 }
965 
966 COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga);
967 
968 static int
command_uga(int argc,char * argv[])969 command_uga(int argc, char *argv[])
970 {
971 	struct efi_fb efifb;
972 
973 	if (uga == NULL) {
974 		snprintf(command_errbuf, sizeof(command_errbuf),
975 		    "%s: UGA Protocol not present", argv[0]);
976 		return (CMD_ERROR);
977 	}
978 
979 	if (argc != 1)
980 		goto usage;
981 
982 	if (efifb_from_uga(&efifb) != CMD_OK) {
983 		snprintf(command_errbuf, sizeof(command_errbuf),
984 		    "%s: Unable to get UGA information", argv[0]);
985 		return (CMD_ERROR);
986 	}
987 
988 	print_efifb(-1, &efifb, 1);
989 	printf("\n");
990 	return (CMD_OK);
991 
992  usage:
993 	snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]);
994 	return (CMD_ERROR);
995 }
996