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 = NULL;
533 for (i = 0; i < nhandles; i++) {
534 EFI_GRAPHICS_OUTPUT_PROTOCOL *tgop;
535 void *dummy;
536
537 status = OpenProtocolByHandle(hlist[i],
538 &gEfiGraphicsOutputProtocolGuid,
539 (void **)&tgop);
540 if (status != EFI_SUCCESS)
541 continue;
542
543 if (tgop->Mode->Info->PixelFormat == PixelBltOnly ||
544 tgop->Mode->Info->PixelFormat >= PixelFormatMax)
545 continue;
546
547 status = OpenProtocolByHandle(hlist[i],
548 &gEfiConsoleOutDeviceGuid, &dummy);
549 if (status == EFI_SUCCESS) {
550 gop_handle = hlist[i];
551 gop = tgop;
552 break;
553 } else if (gop_handle == NULL) {
554 gop_handle = hlist[i];
555 gop = tgop;
556 }
557 }
558
559 free(hlist);
560
561 if (gop != NULL) {
562 /* Save default mode. */
563 if (default_mode == UINT32_MAX) {
564 default_mode = gop->Mode->Mode;
565 }
566 mode = gop_default_mode();
567 if (mode != gop->Mode->Mode)
568 efifb_set_mode(gop, mode);
569 return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info));
570 }
571
572 if (uga != NULL)
573 return (efifb_from_uga(efifb, uga));
574
575 status = BS->LocateProtocol(&gEfiUgaDrawProtocolGuid, NULL,
576 (void **)&uga);
577 if (status == EFI_SUCCESS)
578 return (efifb_from_uga(efifb, uga));
579
580 return (1);
581 }
582
583 static void
print_efifb(int mode,struct efi_fb * efifb,int verbose)584 print_efifb(int mode, struct efi_fb *efifb, int verbose)
585 {
586 uint_t depth;
587 edid_res_list_t res;
588 struct resolution *rp;
589
590 TAILQ_INIT(&res);
591 if (verbose == 1) {
592 printf("Framebuffer mode: %s\n",
593 plat_stdout_is_framebuffer() ? "on" : "off");
594 if (efifb_get_edid(&res)) {
595 printf("EDID");
596 while ((rp = TAILQ_FIRST(&res)) != NULL) {
597 printf(" %dx%d", rp->width, rp->height);
598 TAILQ_REMOVE(&res, rp, next);
599 free(rp);
600 }
601 printf("\n");
602 }
603 }
604
605 if (mode >= 0) {
606 if (verbose == 1)
607 printf("GOP ");
608 printf("mode %d: ", mode);
609 }
610 depth = efifb_color_depth(efifb);
611 printf("%ux%ux%u", efifb->fb_width, efifb->fb_height, depth);
612 if (verbose)
613 printf(", stride=%u", efifb->fb_stride);
614 if (verbose) {
615 printf("\n frame buffer: address=%jx, size=%jx",
616 (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size);
617 printf("\n color mask: R=%08x, G=%08x, B=%08x\n",
618 efifb->fb_mask_red, efifb->fb_mask_green,
619 efifb->fb_mask_blue);
620 if (efifb->fb_addr == 0) {
621 printf("Warning: this mode is not implementing the "
622 "linear framebuffer. The illumos\n\tconsole is "
623 "not available with this mode and will default to "
624 "ttya\n");
625 }
626 }
627 }
628
629 static int
efifb_set_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL * gop,uint_t mode)630 efifb_set_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, uint_t mode)
631 {
632 EFI_STATUS status;
633
634 status = gop->SetMode(gop, mode);
635 if (EFI_ERROR(status)) {
636 snprintf(command_errbuf, sizeof (command_errbuf),
637 "Unable to set mode to %u (error=%lu)",
638 mode, DECODE_ERROR(status));
639 return (CMD_ERROR);
640 }
641 return (CMD_OK);
642 }
643
644 /*
645 * Verify existance of mode number or find mode by
646 * dimensions. If depth is not given, walk values 32, 24, 16, 8.
647 * Return MaxMode if mode is not found.
648 */
649 static int
efifb_find_mode_xydm(UINT32 x,UINT32 y,int depth,int m)650 efifb_find_mode_xydm(UINT32 x, UINT32 y, int depth, int m)
651 {
652 extern EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
653 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
654 EFI_STATUS status;
655 UINTN infosz;
656 struct efi_fb fb;
657 UINT32 mode;
658 uint_t d, i;
659
660 if (m != -1)
661 i = 8;
662 else if (depth == -1)
663 i = 32;
664 else
665 i = depth;
666
667 while (i > 0) {
668 for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
669 status = gop->QueryMode(gop, mode, &infosz, &info);
670 if (EFI_ERROR(status))
671 continue;
672
673 if (m != -1) {
674 if ((UINT32)m == mode)
675 return (mode);
676 else
677 continue;
678 }
679
680 efifb_from_gop(&fb, gop->Mode, info);
681 d = efifb_color_depth(&fb);
682 if (x == fb.fb_width && y == fb.fb_height && d == i)
683 return (mode);
684 }
685
686 if (depth != -1)
687 break;
688
689 i -= 8;
690 }
691
692 return (gop->Mode->MaxMode);
693 }
694
695 static int
efifb_find_mode(char * str)696 efifb_find_mode(char *str)
697 {
698 extern EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
699 int x, y, depth;
700
701 if (!gfx_parse_mode_str(str, &x, &y, &depth))
702 return (gop->Mode->MaxMode);
703
704 return (efifb_find_mode_xydm(x, y, depth, -1));
705 }
706
707 /*
708 * gop_default_mode(). Try to set mode based on EDID.
709 */
710 static uint32_t
gop_default_mode(void)711 gop_default_mode(void)
712 {
713 edid_res_list_t res;
714 struct resolution *rp;
715 extern EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
716 UINT32 mode;
717
718 mode = gop->Mode->MaxMode;
719 TAILQ_INIT(&res);
720 if (efifb_get_edid(&res)) {
721 while ((rp = TAILQ_FIRST(&res)) != NULL) {
722 if (mode == gop->Mode->MaxMode) {
723 mode = efifb_find_mode_xydm(
724 rp->width, rp->height, -1, -1);
725 }
726 TAILQ_REMOVE(&res, rp, next);
727 free(rp);
728 }
729 }
730
731 if (mode == gop->Mode->MaxMode)
732 mode = default_mode;
733
734 return (mode);
735 }
736
737 COMMAND_SET(framebuffer, "framebuffer", "framebuffer mode management",
738 command_gop);
739
740 static int
command_gop(int argc,char * argv[])741 command_gop(int argc, char *argv[])
742 {
743 extern struct efi_fb efifb;
744 extern EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
745 struct efi_fb fb;
746 EFI_STATUS status;
747 char *arg, *cp;
748 uint_t mode;
749
750 if (gop == NULL) {
751 snprintf(command_errbuf, sizeof (command_errbuf),
752 "%s: Graphics Output Protocol not present", argv[0]);
753 return (CMD_ERROR);
754 }
755
756 if (argc < 2)
757 goto usage;
758
759 /*
760 * Note we can not turn the GOP itself off, but instead we instruct
761 * tem to use text mode.
762 */
763 if (strcmp(argv[1], "off") == 0) {
764 if (argc != 2)
765 goto usage;
766
767 reset_font_flags();
768 plat_cons_update_mode(EfiConsoleControlScreenText);
769 return (CMD_OK);
770 }
771
772 /*
773 * Set GOP to use default mode, then notify tem.
774 */
775 if (strcmp(argv[1], "on") == 0) {
776 if (argc != 2)
777 goto usage;
778
779 reset_font_flags();
780 mode = gop_default_mode();
781 if (mode != gop->Mode->Mode)
782 efifb_set_mode(gop, mode);
783
784 plat_cons_update_mode(EfiConsoleControlScreenGraphics);
785 return (CMD_OK);
786 }
787
788 if (strcmp(argv[1], "set") == 0) {
789 int rv;
790
791 if (argc != 3)
792 goto usage;
793
794 arg = argv[2];
795 if (strchr(arg, 'x') == NULL) {
796 errno = 0;
797 mode = strtoul(arg, &cp, 0);
798 if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
799 snprintf(command_errbuf,
800 sizeof (command_errbuf),
801 "mode should be an integer");
802 return (CMD_ERROR);
803 }
804 mode = efifb_find_mode_xydm(0, 0, 0, mode);
805 } else {
806 mode = efifb_find_mode(arg);
807 }
808
809 if (mode == gop->Mode->MaxMode)
810 mode = gop->Mode->Mode;
811
812 reset_font_flags();
813 rv = efifb_set_mode(gop, mode);
814 plat_cons_update_mode(EfiConsoleControlScreenGraphics);
815 return (rv);
816 }
817
818 if (strcmp(argv[1], "get") == 0) {
819 if (argc != 2)
820 goto usage;
821
822 print_efifb(gop->Mode->Mode, &efifb, 1);
823 printf("\n");
824 return (CMD_OK);
825 }
826
827 if (strcmp(argv[1], "list") == 0) {
828 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
829 UINTN infosz;
830 int depth, d = -1;
831
832 if (argc != 2 && argc != 3)
833 goto usage;
834
835 if (argc == 3) {
836 arg = argv[2];
837 errno = 0;
838 d = strtoul(arg, &cp, 0);
839 if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
840 snprintf(command_errbuf,
841 sizeof (command_errbuf),
842 "depth should be an integer");
843 return (CMD_ERROR);
844 }
845 }
846 pager_open();
847 for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
848 status = gop->QueryMode(gop, mode, &infosz, &info);
849 if (EFI_ERROR(status))
850 continue;
851 efifb_from_gop(&fb, gop->Mode, info);
852 depth = efifb_color_depth(&fb);
853 if (d != -1 && d != depth)
854 continue;
855 print_efifb(mode, &fb, 0);
856 if (pager_output("\n"))
857 break;
858 }
859 pager_close();
860 return (CMD_OK);
861 }
862
863 usage:
864 snprintf(command_errbuf, sizeof (command_errbuf),
865 "usage: %s on | off | get | list [depth] | "
866 "set <display or GOP mode number>", argv[0]);
867 return (CMD_ERROR);
868 }
869
870 COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga);
871
872 static int
command_uga(int argc,char * argv[])873 command_uga(int argc, char *argv[])
874 {
875 extern struct efi_fb efifb;
876 extern EFI_UGA_DRAW_PROTOCOL *uga;
877
878 if (uga == NULL) {
879 snprintf(command_errbuf, sizeof (command_errbuf),
880 "%s: UGA Protocol not present", argv[0]);
881 return (CMD_ERROR);
882 }
883
884 if (argc != 1)
885 goto usage;
886
887 if (efifb.fb_addr == 0) {
888 snprintf(command_errbuf, sizeof (command_errbuf),
889 "%s: Unable to get UGA information", argv[0]);
890 return (CMD_ERROR);
891 }
892
893 print_efifb(-1, &efifb, 1);
894 printf("\n");
895 return (CMD_OK);
896
897 usage:
898 snprintf(command_errbuf, sizeof (command_errbuf), "usage: %s", argv[0]);
899 return (CMD_ERROR);
900 }
901