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 <efiuga.h> 42 #include <efipciio.h> 43 #include <Protocol/EdidActive.h> 44 #include <Protocol/EdidDiscovered.h> 45 #include <machine/metadata.h> 46 47 #include "gfx_fb.h" 48 #include "framebuffer.h" 49 50 EFI_GUID conout_guid = EFI_CONSOLE_OUT_DEVICE_GUID; 51 EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; 52 static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID; 53 EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID; 54 static EFI_GUID active_edid_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID; 55 static EFI_GUID discovered_edid_guid = EFI_EDID_DISCOVERED_PROTOCOL_GUID; 56 static EFI_HANDLE gop_handle; 57 58 /* Saved initial GOP mode. */ 59 static uint32_t default_mode = UINT32_MAX; 60 /* Cached EDID. */ 61 struct vesa_edid_info *edid_info = NULL; 62 63 static uint32_t gop_default_mode(void); 64 static int efifb_set_mode(EFI_GRAPHICS_OUTPUT *, uint_t); 65 66 static uint_t 67 efifb_color_depth(struct efi_fb *efifb) 68 { 69 uint32_t mask; 70 uint_t depth; 71 72 mask = efifb->fb_mask_red | efifb->fb_mask_green | 73 efifb->fb_mask_blue | efifb->fb_mask_reserved; 74 if (mask == 0) 75 return (0); 76 for (depth = 1; mask != 1; depth++) 77 mask >>= 1; 78 return (depth); 79 } 80 81 static int 82 efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt, 83 EFI_PIXEL_BITMASK *pixinfo) 84 { 85 int result; 86 87 result = 0; 88 switch (pixfmt) { 89 case PixelRedGreenBlueReserved8BitPerColor: 90 case PixelBltOnly: 91 efifb->fb_mask_red = 0x000000ff; 92 efifb->fb_mask_green = 0x0000ff00; 93 efifb->fb_mask_blue = 0x00ff0000; 94 efifb->fb_mask_reserved = 0xff000000; 95 break; 96 case PixelBlueGreenRedReserved8BitPerColor: 97 efifb->fb_mask_red = 0x00ff0000; 98 efifb->fb_mask_green = 0x0000ff00; 99 efifb->fb_mask_blue = 0x000000ff; 100 efifb->fb_mask_reserved = 0xff000000; 101 break; 102 case PixelBitMask: 103 efifb->fb_mask_red = pixinfo->RedMask; 104 efifb->fb_mask_green = pixinfo->GreenMask; 105 efifb->fb_mask_blue = pixinfo->BlueMask; 106 efifb->fb_mask_reserved = pixinfo->ReservedMask; 107 break; 108 default: 109 result = 1; 110 break; 111 } 112 return (result); 113 } 114 115 static int 116 efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode, 117 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info) 118 { 119 int result; 120 121 efifb->fb_addr = mode->FrameBufferBase; 122 efifb->fb_size = mode->FrameBufferSize; 123 efifb->fb_height = info->VerticalResolution; 124 efifb->fb_width = info->HorizontalResolution; 125 efifb->fb_stride = info->PixelsPerScanLine; 126 result = efifb_mask_from_pixfmt(efifb, info->PixelFormat, 127 &info->PixelInformation); 128 if (efifb->fb_addr == 0) 129 result = 1; 130 return (result); 131 } 132 133 static ssize_t 134 efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, uint_t line, 135 EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size) 136 { 137 EFI_UGA_PIXEL pix0, pix1; 138 uint8_t *data1, *data2; 139 size_t count, maxcount = 1024; 140 ssize_t ofs; 141 EFI_STATUS status; 142 uint_t idx; 143 144 status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer, 145 0, line, 0, 0, 1, 1, 0); 146 if (EFI_ERROR(status)) { 147 printf("UGA BLT operation failed (video->buffer)"); 148 return (-1); 149 } 150 pix1.Red = ~pix0.Red; 151 pix1.Green = ~pix0.Green; 152 pix1.Blue = ~pix0.Blue; 153 pix1.Reserved = 0; 154 155 data1 = calloc(maxcount, 2); 156 if (data1 == NULL) { 157 printf("Unable to allocate memory"); 158 return (-1); 159 } 160 data2 = data1 + maxcount; 161 162 ofs = 0; 163 while (size > 0) { 164 count = min(size, maxcount); 165 166 status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, 167 EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, 168 data1); 169 if (EFI_ERROR(status)) { 170 printf("Error reading frame buffer (before)"); 171 goto fail; 172 } 173 status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo, 174 0, 0, 0, line, 1, 1, 0); 175 if (EFI_ERROR(status)) { 176 printf("UGA BLT operation failed (modify)"); 177 goto fail; 178 } 179 status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, 180 EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, 181 data2); 182 if (EFI_ERROR(status)) { 183 printf("Error reading frame buffer (after)"); 184 goto fail; 185 } 186 status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo, 187 0, 0, 0, line, 1, 1, 0); 188 if (EFI_ERROR(status)) { 189 printf("UGA BLT operation failed (restore)"); 190 goto fail; 191 } 192 for (idx = 0; idx < count; idx++) { 193 if (data1[idx] != data2[idx]) { 194 free(data1); 195 return (ofs + (idx & ~3)); 196 } 197 } 198 ofs += count; 199 size -= count; 200 } 201 printf("No change detected in frame buffer"); 202 203 fail: 204 printf(" -- error %lu\n", EFI_ERROR_CODE(status)); 205 free(data1); 206 return (-1); 207 } 208 209 static EFI_PCI_IO_PROTOCOL * 210 efifb_uga_get_pciio(void) 211 { 212 EFI_PCI_IO_PROTOCOL *pciio; 213 EFI_HANDLE *handles; 214 EFI_STATUS status; 215 uint_t i, nhandles; 216 217 /* Get all handles that support the UGA protocol. */ 218 status = efi_get_protocol_handles(&uga_guid, &nhandles, &handles); 219 if (status != EFI_SUCCESS) { 220 return (NULL); 221 } 222 223 /* Get the PCI I/O interface of the first handle that supports it. */ 224 pciio = NULL; 225 for (i = 0; i < nhandles; i++) { 226 status = OpenProtocolByHandle(handles[i], &pciio_guid, 227 (void **)&pciio); 228 if (status == EFI_SUCCESS) { 229 free(handles); 230 return (pciio); 231 } 232 } 233 free(handles); 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 uint_t 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.") == 0) { 328 ev = getenv("smbios.system.product"); 329 if (ev != NULL && strcmp(ev, "iMac7,1") == 0) { 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") == 0) { 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 h) 450 { 451 const uint8_t magic[] = EDID_MAGIC; 452 EFI_EDID_ACTIVE_PROTOCOL *edid; 453 struct vesa_edid_info *edid_infop; 454 EFI_GUID *guid; 455 EFI_STATUS status; 456 size_t size; 457 458 guid = &active_edid_guid; 459 status = BS->OpenProtocol(h, guid, (void **)&edid, IH, NULL, 460 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 461 if (status != EFI_SUCCESS || 462 edid->SizeOfEdid == 0) { 463 guid = &discovered_edid_guid; 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 return (NULL); 469 } 470 471 size = MAX(sizeof (*edid_infop), edid->SizeOfEdid); 472 473 edid_infop = calloc(1, size); 474 if (edid_infop == NULL) 475 return (NULL); 476 477 memcpy(edid_infop, edid->Edid, edid->SizeOfEdid); 478 479 /* Validate EDID */ 480 if (memcmp(edid_infop, magic, sizeof (magic)) != 0) 481 goto error; 482 483 if (edid_infop->header.version != 1) 484 goto error; 485 486 return (edid_infop); 487 error: 488 free(edid_infop); 489 return (NULL); 490 } 491 492 static bool 493 efifb_get_edid(edid_res_list_t *res) 494 { 495 bool rv = false; 496 497 if (edid_info == NULL) 498 edid_info = efifb_gop_get_edid(gop_handle); 499 500 if (edid_info != NULL) 501 rv = gfx_get_edid_resolution(edid_info, res); 502 503 return (rv); 504 } 505 506 int 507 efi_find_framebuffer(struct efi_fb *efifb) 508 { 509 EFI_HANDLE *hlist; 510 uint_t nhandles, i; 511 extern EFI_GRAPHICS_OUTPUT *gop; 512 extern EFI_UGA_DRAW_PROTOCOL *uga; 513 EFI_STATUS status; 514 uint32_t mode; 515 516 if (gop != NULL) 517 return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info)); 518 519 status = efi_get_protocol_handles(&gop_guid, &nhandles, &hlist); 520 if (EFI_ERROR(status)) 521 return (efi_status_to_errno(status)); 522 523 /* 524 * Search for ConOut protocol, if not found, use first handle. 525 */ 526 gop_handle = *hlist; 527 for (i = 0; i < nhandles; i++) { 528 void *dummy = NULL; 529 530 status = OpenProtocolByHandle(hlist[i], &conout_guid, &dummy); 531 if (status == EFI_SUCCESS) { 532 gop_handle = hlist[i]; 533 break; 534 } 535 } 536 537 status = OpenProtocolByHandle(gop_handle, &gop_guid, (void **)&gop); 538 free(hlist); 539 540 if (status == EFI_SUCCESS) { 541 /* Save default mode. */ 542 if (default_mode == UINT32_MAX) { 543 default_mode = gop->Mode->Mode; 544 } 545 mode = gop_default_mode(); 546 if (mode != gop->Mode->Mode) 547 efifb_set_mode(gop, mode); 548 return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info)); 549 } 550 551 if (uga != NULL) 552 return (efifb_from_uga(efifb, uga)); 553 554 status = BS->LocateProtocol(&uga_guid, NULL, (void **)&uga); 555 if (status == EFI_SUCCESS) 556 return (efifb_from_uga(efifb, uga)); 557 558 return (1); 559 } 560 561 static void 562 print_efifb(int mode, struct efi_fb *efifb, int verbose) 563 { 564 uint_t depth; 565 edid_res_list_t res; 566 struct resolution *rp; 567 568 TAILQ_INIT(&res); 569 if (verbose == 1) { 570 printf("Framebuffer mode: %s\n", 571 plat_stdout_is_framebuffer() ? "on" : "off"); 572 if (efifb_get_edid(&res)) { 573 printf("EDID"); 574 while ((rp = TAILQ_FIRST(&res)) != NULL) { 575 printf(" %dx%d", rp->width, rp->height); 576 TAILQ_REMOVE(&res, rp, next); 577 free(rp); 578 } 579 printf("\n"); 580 } 581 } 582 583 if (mode >= 0) { 584 if (verbose == 1) 585 printf("GOP "); 586 printf("mode %d: ", mode); 587 } 588 depth = efifb_color_depth(efifb); 589 printf("%ux%ux%u", efifb->fb_width, efifb->fb_height, depth); 590 if (verbose) 591 printf(", stride=%u", efifb->fb_stride); 592 if (verbose) { 593 printf("\n frame buffer: address=%jx, size=%jx", 594 (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size); 595 printf("\n color mask: R=%08x, G=%08x, B=%08x\n", 596 efifb->fb_mask_red, efifb->fb_mask_green, 597 efifb->fb_mask_blue); 598 if (efifb->fb_addr == 0) { 599 printf("Warning: this mode is not implementing the " 600 "linear framebuffer. The illumos\n\tconsole is " 601 "not available with this mode and will default to " 602 "ttya\n"); 603 } 604 } 605 } 606 607 static int 608 efifb_set_mode(EFI_GRAPHICS_OUTPUT *gop, uint_t mode) 609 { 610 EFI_STATUS status; 611 612 status = gop->SetMode(gop, mode); 613 if (EFI_ERROR(status)) { 614 snprintf(command_errbuf, sizeof (command_errbuf), 615 "Unable to set mode to %u (error=%lu)", 616 mode, EFI_ERROR_CODE(status)); 617 return (CMD_ERROR); 618 } 619 return (CMD_OK); 620 } 621 622 /* 623 * Verify existance of mode number or find mode by 624 * dimensions. If depth is not given, walk values 32, 24, 16, 8. 625 * Return MaxMode if mode is not found. 626 */ 627 static int 628 efifb_find_mode_xydm(UINT32 x, UINT32 y, int depth, int m) 629 { 630 extern EFI_GRAPHICS_OUTPUT *gop; 631 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; 632 EFI_STATUS status; 633 UINTN infosz; 634 struct efi_fb fb; 635 UINT32 mode; 636 uint_t d, i; 637 638 if (m != -1) 639 i = 8; 640 else if (depth == -1) 641 i = 32; 642 else 643 i = depth; 644 645 while (i > 0) { 646 for (mode = 0; mode < gop->Mode->MaxMode; mode++) { 647 status = gop->QueryMode(gop, mode, &infosz, &info); 648 if (EFI_ERROR(status)) 649 continue; 650 651 if (m != -1) { 652 if ((UINT32)m == mode) 653 return (mode); 654 else 655 continue; 656 } 657 658 efifb_from_gop(&fb, gop->Mode, info); 659 d = efifb_color_depth(&fb); 660 if (x == fb.fb_width && y == fb.fb_height && d == i) 661 return (mode); 662 } 663 664 if (depth != -1) 665 break; 666 667 i -= 8; 668 } 669 670 return (gop->Mode->MaxMode); 671 } 672 673 static int 674 efifb_find_mode(char *str) 675 { 676 extern EFI_GRAPHICS_OUTPUT *gop; 677 int x, y, depth; 678 679 if (!gfx_parse_mode_str(str, &x, &y, &depth)) 680 return (gop->Mode->MaxMode); 681 682 return (efifb_find_mode_xydm(x, y, depth, -1)); 683 } 684 685 /* 686 * gop_default_mode(). Try to set mode based on EDID. 687 */ 688 static uint32_t 689 gop_default_mode(void) 690 { 691 edid_res_list_t res; 692 struct resolution *rp; 693 extern EFI_GRAPHICS_OUTPUT *gop; 694 UINT32 mode; 695 696 mode = gop->Mode->MaxMode; 697 TAILQ_INIT(&res); 698 if (efifb_get_edid(&res)) { 699 while ((rp = TAILQ_FIRST(&res)) != NULL) { 700 if (mode == gop->Mode->MaxMode) { 701 mode = efifb_find_mode_xydm( 702 rp->width, rp->height, -1, -1); 703 } 704 TAILQ_REMOVE(&res, rp, next); 705 free(rp); 706 } 707 } 708 709 if (mode == gop->Mode->MaxMode) 710 mode = default_mode; 711 712 return (mode); 713 } 714 715 COMMAND_SET(framebuffer, "framebuffer", "framebuffer mode management", 716 command_gop); 717 718 static int 719 command_gop(int argc, char *argv[]) 720 { 721 extern struct efi_fb efifb; 722 extern EFI_GRAPHICS_OUTPUT *gop; 723 struct efi_fb fb; 724 EFI_STATUS status; 725 char *arg, *cp; 726 uint_t mode; 727 728 if (gop == NULL) { 729 snprintf(command_errbuf, sizeof (command_errbuf), 730 "%s: Graphics Output Protocol not present", argv[0]); 731 return (CMD_ERROR); 732 } 733 734 if (argc < 2) 735 goto usage; 736 737 /* 738 * Note we can not turn the GOP itself off, but instead we instruct 739 * tem to use text mode. 740 */ 741 if (strcmp(argv[1], "off") == 0) { 742 if (argc != 2) 743 goto usage; 744 745 reset_font_flags(); 746 plat_cons_update_mode(EfiConsoleControlScreenText); 747 return (CMD_OK); 748 } 749 750 /* 751 * Set GOP to use default mode, then notify tem. 752 */ 753 if (strcmp(argv[1], "on") == 0) { 754 if (argc != 2) 755 goto usage; 756 757 reset_font_flags(); 758 mode = gop_default_mode(); 759 if (mode != gop->Mode->Mode) 760 efifb_set_mode(gop, mode); 761 762 plat_cons_update_mode(EfiConsoleControlScreenGraphics); 763 return (CMD_OK); 764 } 765 766 if (strcmp(argv[1], "set") == 0) { 767 int rv; 768 769 if (argc != 3) 770 goto usage; 771 772 arg = argv[2]; 773 if (strchr(arg, 'x') == NULL) { 774 errno = 0; 775 mode = strtoul(arg, &cp, 0); 776 if (errno != 0 || *arg == '\0' || cp[0] != '\0') { 777 snprintf(command_errbuf, 778 sizeof (command_errbuf), 779 "mode should be an integer"); 780 return (CMD_ERROR); 781 } 782 mode = efifb_find_mode_xydm(0, 0, 0, mode); 783 } else { 784 mode = efifb_find_mode(arg); 785 } 786 787 if (mode == gop->Mode->MaxMode) 788 mode = gop->Mode->Mode; 789 790 reset_font_flags(); 791 rv = efifb_set_mode(gop, mode); 792 plat_cons_update_mode(EfiConsoleControlScreenGraphics); 793 return (rv); 794 } 795 796 if (strcmp(argv[1], "get") == 0) { 797 if (argc != 2) 798 goto usage; 799 800 print_efifb(gop->Mode->Mode, &efifb, 1); 801 printf("\n"); 802 return (CMD_OK); 803 } 804 805 if (strcmp(argv[1], "list") == 0) { 806 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; 807 UINTN infosz; 808 int depth, d = -1; 809 810 if (argc != 2 && argc != 3) 811 goto usage; 812 813 if (argc == 3) { 814 arg = argv[2]; 815 errno = 0; 816 d = strtoul(arg, &cp, 0); 817 if (errno != 0 || *arg == '\0' || cp[0] != '\0') { 818 snprintf(command_errbuf, 819 sizeof (command_errbuf), 820 "depth should be an integer"); 821 return (CMD_ERROR); 822 } 823 } 824 pager_open(); 825 for (mode = 0; mode < gop->Mode->MaxMode; mode++) { 826 status = gop->QueryMode(gop, mode, &infosz, &info); 827 if (EFI_ERROR(status)) 828 continue; 829 efifb_from_gop(&fb, gop->Mode, info); 830 depth = efifb_color_depth(&fb); 831 if (d != -1 && d != depth) 832 continue; 833 print_efifb(mode, &fb, 0); 834 if (pager_output("\n")) 835 break; 836 } 837 pager_close(); 838 return (CMD_OK); 839 } 840 841 usage: 842 snprintf(command_errbuf, sizeof (command_errbuf), 843 "usage: %s on | off | get | list [depth] | " 844 "set <display or GOP mode number>", argv[0]); 845 return (CMD_ERROR); 846 } 847 848 COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga); 849 850 static int 851 command_uga(int argc, char *argv[]) 852 { 853 extern struct efi_fb efifb; 854 extern EFI_UGA_DRAW_PROTOCOL *uga; 855 856 if (uga == NULL) { 857 snprintf(command_errbuf, sizeof (command_errbuf), 858 "%s: UGA Protocol not present", argv[0]); 859 return (CMD_ERROR); 860 } 861 862 if (argc != 1) 863 goto usage; 864 865 if (efifb.fb_addr == 0) { 866 snprintf(command_errbuf, sizeof (command_errbuf), 867 "%s: Unable to get UGA information", argv[0]); 868 return (CMD_ERROR); 869 } 870 871 print_efifb(-1, &efifb, 1); 872 printf("\n"); 873 return (CMD_OK); 874 875 usage: 876 snprintf(command_errbuf, sizeof (command_errbuf), "usage: %s", argv[0]); 877 return (CMD_ERROR); 878 } 879