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