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 } else if (ev != NULL && !strcmp(ev, "MacBookPro3,1")) {
396 /*
397 * Valid for MacBookPro 17" with standard resolution.
398 * Other Models are:
399 * MacBookPro 15" with horiz=1440
400 * MacBookPro 17" with horiz=1920
401 */
402
403 /* These are the expected values we should have. */
404 if (horiz == 1680) {
405 vert = 1050;
406 fbaddr = 0xc0000000;
407 /* These are the missing bits. */
408 stride = 2048;
409 /* 24 scan lines down */
410 offset = stride * 4 * 24;
411 }
412 }
413 }
414
415 /*
416 * If this is hardware we know, make sure that it looks familiar
417 * before we accept our hardcoded values.
418 */
419 if (offset >= 0 && efifb->fb_width == horiz &&
420 efifb->fb_height == vert && efifb->fb_addr == fbaddr) {
421 efifb->fb_addr += offset;
422 efifb->fb_size -= offset;
423 efifb->fb_stride = stride;
424 return (0);
425 } else if (offset >= 0) {
426 printf("Hardware make/model known, but graphics not "
427 "as expected.\n");
428 printf("Console may not work!\n");
429 }
430
431 /*
432 * The stride is equal or larger to the width. Often it's the
433 * next larger power of two. We'll start with that...
434 */
435 efifb->fb_stride = efifb->fb_width;
436 do {
437 np = efifb->fb_stride & (efifb->fb_stride - 1);
438 if (np) {
439 efifb->fb_stride |= (np - 1);
440 efifb->fb_stride++;
441 }
442 } while (np);
443
444 ev = getenv("hw.efifb.address");
445 if (ev == NULL) {
446 if (efifb->fb_addr == 0) {
447 printf("Please set hw.efifb.address and "
448 "hw.efifb.stride.\n");
449 return (1);
450 }
451
452 /*
453 * The visible part of the frame buffer may not start at
454 * offset 0, so try to detect it. Note that we may not
455 * always be able to read from the frame buffer, which
456 * means that we may not be able to detect anything. In
457 * that case, we would take a long time scanning for a
458 * pixel change in the frame buffer, which would have it
459 * appear that we're hanging, so we limit the scan to
460 * 1/256th of the frame buffer. This number is mostly
461 * based on PR 202730 and the fact that on a MacBoook,
462 * where we can't read from the frame buffer the offset
463 * of the visible region is 0. In short: we want to scan
464 * enough to handle all adapters that have an offset
465 * larger than 0 and we want to scan as little as we can
466 * to not appear to hang when we can't read from the
467 * frame buffer.
468 */
469 offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr,
470 efifb->fb_size >> 8);
471 if (offset == -1) {
472 printf("Unable to reliably detect frame buffer.\n");
473 } else if (offset > 0) {
474 efifb->fb_addr += offset;
475 efifb->fb_size -= offset;
476 }
477 } else {
478 offset = 0;
479 efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
480 efifb->fb_addr = strtoul(ev, &p, 0);
481 if (*p != '\0')
482 return (1);
483 }
484
485 ev = getenv("hw.efifb.stride");
486 if (ev == NULL) {
487 if (pciio != NULL && offset != -1) {
488 /* Determine the stride. */
489 offset = efifb_uga_find_pixel(uga, 1, pciio,
490 efifb->fb_addr, horiz * 8);
491 if (offset != -1)
492 efifb->fb_stride = offset >> 2;
493 } else {
494 printf("Unable to reliably detect the stride.\n");
495 }
496 } else {
497 efifb->fb_stride = strtoul(ev, &p, 0);
498 if (*p != '\0')
499 return (1);
500 }
501
502 /*
503 * We finalized on the stride, so recalculate the size of the
504 * frame buffer.
505 */
506 efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
507 return (0);
508 }
509
510 /*
511 * Fetch EDID info. Caller must free the buffer.
512 */
513 static struct vesa_edid_info *
efifb_gop_get_edid(EFI_HANDLE h)514 efifb_gop_get_edid(EFI_HANDLE h)
515 {
516 const uint8_t magic[] = EDID_MAGIC;
517 EFI_EDID_ACTIVE_PROTOCOL *edid;
518 struct vesa_edid_info *edid_infop;
519 EFI_GUID *guid;
520 EFI_STATUS status;
521 size_t size;
522
523 guid = &active_edid_guid;
524 status = BS->OpenProtocol(h, guid, (void **)&edid, IH, NULL,
525 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
526 if (status != EFI_SUCCESS ||
527 edid->SizeOfEdid == 0) {
528 guid = &discovered_edid_guid;
529 status = BS->OpenProtocol(h, guid, (void **)&edid, IH, NULL,
530 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
531 if (status != EFI_SUCCESS ||
532 edid->SizeOfEdid == 0)
533 return (NULL);
534 }
535
536 size = MAX(sizeof(*edid_infop), edid->SizeOfEdid);
537
538 edid_infop = calloc(1, size);
539 if (edid_infop == NULL)
540 return (NULL);
541
542 memcpy(edid_infop, edid->Edid, edid->SizeOfEdid);
543
544 /* Validate EDID */
545 if (memcmp(edid_infop, magic, sizeof (magic)) != 0)
546 goto error;
547
548 if (edid_infop->header.version != 1)
549 goto error;
550
551 return (edid_infop);
552 error:
553 free(edid_infop);
554 return (NULL);
555 }
556
557 static bool
efifb_get_edid(edid_res_list_t * res)558 efifb_get_edid(edid_res_list_t *res)
559 {
560 bool rv = false;
561
562 if (edid_info == NULL)
563 edid_info = efifb_gop_get_edid(gop_handle);
564
565 if (edid_info != NULL)
566 rv = gfx_get_edid_resolution(edid_info, res);
567
568 return (rv);
569 }
570
571 bool
efi_has_gop(void)572 efi_has_gop(void)
573 {
574 EFI_STATUS status;
575 EFI_HANDLE *hlist;
576 UINTN hsize;
577
578 hsize = 0;
579 hlist = NULL;
580 status = BS->LocateHandle(ByProtocol, &gop_guid, NULL, &hsize, hlist);
581
582 return (status == EFI_BUFFER_TOO_SMALL);
583 }
584
585
586 int
efi_find_framebuffer(teken_gfx_t * gfx_state)587 efi_find_framebuffer(teken_gfx_t *gfx_state)
588 {
589 EFI_PHYSICAL_ADDRESS ptr;
590 EFI_HANDLE *hlist;
591 UINTN nhandles, i, hsize;
592 struct efi_fb efifb;
593 EFI_STATUS status;
594 int rv;
595
596 gfx_state->tg_fb_type = FB_TEXT;
597
598 hsize = 0;
599 hlist = NULL;
600 status = BS->LocateHandle(ByProtocol, &gop_guid, NULL, &hsize, hlist);
601 if (status == EFI_BUFFER_TOO_SMALL) {
602 hlist = malloc(hsize);
603 if (hlist == NULL)
604 return (ENOMEM);
605 status = BS->LocateHandle(ByProtocol, &gop_guid, NULL, &hsize,
606 hlist);
607 if (EFI_ERROR(status))
608 free(hlist);
609 }
610
611 if (EFI_ERROR(status)) {
612 status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
613 if (status == EFI_SUCCESS) {
614 gfx_state->tg_fb_type = FB_UGA;
615 gfx_state->tg_private = uga;
616 } else {
617 return (efi_status_to_errno(status));
618 }
619 } else {
620 nhandles = hsize / sizeof(*hlist);
621
622 /*
623 * Search for ConOut protocol, if not found, use first handle.
624 */
625 gop_handle = NULL;
626 for (i = 0; i < nhandles; i++) {
627 EFI_GRAPHICS_OUTPUT_PROTOCOL *tgop;
628 void *dummy;
629
630 status = OpenProtocolByHandle(hlist[i], &gop_guid,
631 (void **)&tgop);
632 if (status != EFI_SUCCESS)
633 continue;
634
635 /*
636 * Some platforms half initialize the graphics protocol
637 * here when no monitor present, even though spec says
638 * they are non-optional. Windows and Linux don't access
639 * Mode->Info generally, so such bugs can slip through.
640 */
641 if (tgop->Mode == NULL || tgop->Mode->Info == NULL ||
642 tgop->Mode->Info->PixelFormat == PixelBltOnly ||
643 tgop->Mode->Info->PixelFormat >= PixelFormatMax)
644 continue;
645
646 status = OpenProtocolByHandle(hlist[i], &conout_guid,
647 &dummy);
648 if (status == EFI_SUCCESS) {
649 gop_handle = hlist[i];
650 gop = tgop;
651 break;
652 } else if (gop_handle == NULL) {
653 gop_handle = hlist[i];
654 gop = tgop;
655 }
656 }
657
658 free(hlist);
659 if (gop_handle == NULL)
660 return (ENXIO);
661
662 gfx_state->tg_fb_type = FB_GOP;
663 gfx_state->tg_private = gop;
664 if (edid_info == NULL)
665 edid_info = efifb_gop_get_edid(gop_handle);
666 }
667
668 switch (gfx_state->tg_fb_type) {
669 case FB_GOP:
670 rv = efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
671 break;
672
673 case FB_UGA:
674 rv = efifb_from_uga(&efifb);
675 break;
676
677 default:
678 return (EINVAL);
679 }
680
681 if (rv != 0)
682 return (rv);
683
684 gfx_state->tg_fb.fb_addr = efifb.fb_addr;
685 gfx_state->tg_fb.fb_size = efifb.fb_size;
686 gfx_state->tg_fb.fb_height = efifb.fb_height;
687 gfx_state->tg_fb.fb_width = efifb.fb_width;
688 gfx_state->tg_fb.fb_stride = efifb.fb_stride;
689 gfx_state->tg_fb.fb_mask_red = efifb.fb_mask_red;
690 gfx_state->tg_fb.fb_mask_green = efifb.fb_mask_green;
691 gfx_state->tg_fb.fb_mask_blue = efifb.fb_mask_blue;
692 gfx_state->tg_fb.fb_mask_reserved = efifb.fb_mask_reserved;
693
694 gfx_state->tg_fb.fb_bpp = fls(efifb.fb_mask_red | efifb.fb_mask_green |
695 efifb.fb_mask_blue | efifb.fb_mask_reserved);
696
697 if (gfx_state->tg_shadow_fb != NULL)
698 BS->FreePages((uintptr_t)gfx_state->tg_shadow_fb,
699 gfx_state->tg_shadow_sz);
700 gfx_state->tg_shadow_sz =
701 EFI_SIZE_TO_PAGES(efifb.fb_height * efifb.fb_width *
702 sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
703 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
704 gfx_state->tg_shadow_sz, &ptr);
705 gfx_state->tg_shadow_fb = status == EFI_SUCCESS ?
706 (uint32_t *)(uintptr_t)ptr : NULL;
707
708 return (0);
709 }
710
711 static void
print_efifb(int mode,struct efi_fb * efifb,int verbose)712 print_efifb(int mode, struct efi_fb *efifb, int verbose)
713 {
714 u_int depth;
715
716 if (mode >= 0)
717 printf("mode %d: ", mode);
718 depth = efifb_color_depth(efifb);
719 printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height,
720 depth, efifb->fb_stride);
721 if (verbose) {
722 printf("\n frame buffer: address=%jx, size=%jx",
723 (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size);
724 printf("\n color mask: R=%08x, G=%08x, B=%08x\n",
725 efifb->fb_mask_red, efifb->fb_mask_green,
726 efifb->fb_mask_blue);
727 }
728 }
729
730 static bool
efi_resolution_compare(struct named_resolution * res,const char * cmp)731 efi_resolution_compare(struct named_resolution *res, const char *cmp)
732 {
733
734 if (strcasecmp(res->name, cmp) == 0)
735 return (true);
736 if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0)
737 return (true);
738 return (false);
739 }
740
741
742 static void
efi_get_max_resolution(int * width,int * height)743 efi_get_max_resolution(int *width, int *height)
744 {
745 struct named_resolution *res;
746 char *maxres;
747 char *height_start, *width_start;
748 int idx;
749
750 *width = *height = 0;
751 maxres = getenv("efi_max_resolution");
752 /* No max_resolution set? Bail out; choose highest resolution */
753 if (maxres == NULL)
754 return;
755 /* See if it matches one of our known resolutions */
756 for (idx = 0; idx < nitems(resolutions); ++idx) {
757 res = &resolutions[idx];
758 if (efi_resolution_compare(res, maxres)) {
759 *width = res->width;
760 *height = res->height;
761 return;
762 }
763 }
764 /* Not a known resolution, try to parse it; make a copy we can modify */
765 maxres = strdup(maxres);
766 if (maxres == NULL)
767 return;
768 height_start = strchr(maxres, 'x');
769 if (height_start == NULL) {
770 free(maxres);
771 return;
772 }
773 width_start = maxres;
774 *height_start++ = 0;
775 /* Errors from this will effectively mean "no max" */
776 *width = (int)strtol(width_start, NULL, 0);
777 *height = (int)strtol(height_start, NULL, 0);
778 free(maxres);
779 }
780
781 static int
gop_autoresize(void)782 gop_autoresize(void)
783 {
784 struct efi_fb efifb;
785 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
786 EFI_STATUS status;
787 UINTN infosz;
788 UINT32 best_mode, currdim, maxdim, mode;
789 int height, max_height, max_width, width;
790
791 best_mode = maxdim = 0;
792 efi_get_max_resolution(&max_width, &max_height);
793 for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
794 status = gop->QueryMode(gop, mode, &infosz, &info);
795 if (EFI_ERROR(status))
796 continue;
797 efifb_from_gop(&efifb, gop->Mode, info);
798 width = info->HorizontalResolution;
799 height = info->VerticalResolution;
800 currdim = width * height;
801 if (currdim > maxdim) {
802 if ((max_width != 0 && width > max_width) ||
803 (max_height != 0 && height > max_height))
804 continue;
805 maxdim = currdim;
806 best_mode = mode;
807 }
808 }
809
810 if (maxdim != 0) {
811 status = gop->SetMode(gop, best_mode);
812 if (EFI_ERROR(status)) {
813 snprintf(command_errbuf, sizeof(command_errbuf),
814 "gop_autoresize: Unable to set mode to %u (error=%lu)",
815 mode, DECODE_ERROR(status));
816 return (CMD_ERROR);
817 }
818 (void) cons_update_mode(true);
819 }
820 return (CMD_OK);
821 }
822
823 static int
text_autoresize()824 text_autoresize()
825 {
826 SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
827 EFI_STATUS status;
828 UINTN i, max_dim, best_mode, cols, rows;
829
830 conout = ST->ConOut;
831 max_dim = best_mode = 0;
832 for (i = 0; i < conout->Mode->MaxMode; i++) {
833 status = conout->QueryMode(conout, i, &cols, &rows);
834 if (EFI_ERROR(status))
835 continue;
836 if (cols * rows > max_dim) {
837 max_dim = cols * rows;
838 best_mode = i;
839 }
840 }
841 if (max_dim > 0)
842 conout->SetMode(conout, best_mode);
843 (void) cons_update_mode(true);
844 return (CMD_OK);
845 }
846
847 static int
uga_autoresize(void)848 uga_autoresize(void)
849 {
850
851 return (text_autoresize());
852 }
853
854 COMMAND_SET(efi_autoresize, "efi-autoresizecons", "EFI Auto-resize Console", command_autoresize);
855
856 static int
command_autoresize(int argc,char * argv[])857 command_autoresize(int argc, char *argv[])
858 {
859 char *textmode;
860
861 textmode = getenv("hw.vga.textmode");
862 /* If it's set and non-zero, we'll select a console mode instead */
863 if (textmode != NULL && strcmp(textmode, "0") != 0)
864 return (text_autoresize());
865
866 if (gop != NULL)
867 return (gop_autoresize());
868
869 if (uga != NULL)
870 return (uga_autoresize());
871
872 snprintf(command_errbuf, sizeof(command_errbuf),
873 "%s: Neither Graphics Output Protocol nor Universal Graphics Adapter present",
874 argv[0]);
875
876 /*
877 * Default to text_autoresize if we have neither GOP or UGA. This won't
878 * give us the most ideal resolution, but it will at least leave us
879 * functional rather than failing the boot for an objectively bad
880 * reason.
881 */
882 return (text_autoresize());
883 }
884
885 COMMAND_SET(gop, "gop", "graphics output protocol", command_gop);
886
887 static int
command_gop(int argc,char * argv[])888 command_gop(int argc, char *argv[])
889 {
890 struct efi_fb efifb;
891 EFI_STATUS status;
892 u_int mode;
893 extern bool ignore_gop_blt;
894
895 if (gop == NULL) {
896 snprintf(command_errbuf, sizeof(command_errbuf),
897 "%s: Graphics Output Protocol not present", argv[0]);
898 return (CMD_ERROR);
899 }
900
901 if (argc < 2)
902 goto usage;
903
904 if (strcmp(argv[1], "set") == 0) {
905 char *cp;
906
907 if (argc != 3)
908 goto usage;
909 mode = strtol(argv[2], &cp, 0);
910 if (cp[0] != '\0') {
911 sprintf(command_errbuf, "mode is an integer");
912 return (CMD_ERROR);
913 }
914 status = gop->SetMode(gop, mode);
915 if (EFI_ERROR(status)) {
916 snprintf(command_errbuf, sizeof(command_errbuf),
917 "%s: Unable to set mode to %u (error=%lu)",
918 argv[0], mode, DECODE_ERROR(status));
919 return (CMD_ERROR);
920 }
921 (void) cons_update_mode(true);
922 } else if (strcmp(argv[1], "blt") == 0) {
923 /*
924 * "blt on" does allow gop->Blt() to be used (default).
925 * "blt off" does block gop->Blt() to be used and use
926 * software rendering instead.
927 */
928 if (argc != 3)
929 goto usage;
930 if (strcmp(argv[2], "on") == 0)
931 ignore_gop_blt = false;
932 else if (strcmp(argv[2], "off") == 0)
933 ignore_gop_blt = true;
934 else
935 goto usage;
936 } else if (strcmp(argv[1], "off") == 0) {
937 /*
938 * Tell console to use SimpleTextOutput protocol.
939 * This means that we do not render the glyphs, but rely on
940 * UEFI firmware to draw on ConsOut device(s).
941 */
942 (void) cons_update_mode(false);
943 } else if (strcmp(argv[1], "get") == 0) {
944 edid_res_list_t res;
945
946 if (argc != 2)
947 goto usage;
948 TAILQ_INIT(&res);
949 efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
950 if (efifb_get_edid(&res)) {
951 struct resolution *rp;
952
953 printf("EDID");
954 while ((rp = TAILQ_FIRST(&res)) != NULL) {
955 printf(" %dx%d", rp->width, rp->height);
956 TAILQ_REMOVE(&res, rp, next);
957 free(rp);
958 }
959 printf("\n");
960 } else {
961 printf("no EDID information\n");
962 }
963 print_efifb(gop->Mode->Mode, &efifb, 1);
964 printf("\n");
965 } else if (strcmp(argv[1], "list") == 0) {
966 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
967 UINTN infosz;
968
969 if (argc != 2)
970 goto usage;
971
972 pager_open();
973 for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
974 status = gop->QueryMode(gop, mode, &infosz, &info);
975 if (EFI_ERROR(status))
976 continue;
977 efifb_from_gop(&efifb, gop->Mode, info);
978 print_efifb(mode, &efifb, 0);
979 if (pager_output("\n"))
980 break;
981 }
982 pager_close();
983 }
984 return (CMD_OK);
985
986 usage:
987 snprintf(command_errbuf, sizeof(command_errbuf),
988 "usage: %s [list | get | set <mode> | off | blt <on|off>]", argv[0]);
989 return (CMD_ERROR);
990 }
991
992 COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga);
993
994 static int
command_uga(int argc,char * argv[])995 command_uga(int argc, char *argv[])
996 {
997 struct efi_fb efifb;
998
999 if (uga == NULL) {
1000 snprintf(command_errbuf, sizeof(command_errbuf),
1001 "%s: UGA Protocol not present", argv[0]);
1002 return (CMD_ERROR);
1003 }
1004
1005 if (argc != 1)
1006 goto usage;
1007
1008 if (efifb_from_uga(&efifb) != CMD_OK) {
1009 snprintf(command_errbuf, sizeof(command_errbuf),
1010 "%s: Unable to get UGA information", argv[0]);
1011 return (CMD_ERROR);
1012 }
1013
1014 print_efifb(-1, &efifb, 1);
1015 printf("\n");
1016 return (CMD_OK);
1017
1018 usage:
1019 snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]);
1020 return (CMD_ERROR);
1021 }
1022