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