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