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