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 * $FreeBSD: head/sys/boot/efi/loader/arch/amd64/framebuffer.c 304532 2016-08-20 16:23:19Z tsoome $ 29 */ 30 31 #include <bootstrap.h> 32 #include <sys/endian.h> 33 #include <stand.h> 34 35 #include <efi.h> 36 #include <efilib.h> 37 38 #include <machine/metadata.h> 39 40 #include "framebuffer.h" 41 42 static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; 43 static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID; 44 static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID; 45 46 u_int 47 efi_framebuffer_bpp(struct efi_fb *efifb) 48 { 49 uint32_t mask; 50 u_int depth; 51 52 mask = efifb->fb_mask_red | efifb->fb_mask_green | 53 efifb->fb_mask_blue | efifb->fb_mask_reserved; 54 if (mask == 0) 55 return (0); 56 for (depth = 1; mask != 1; depth++) 57 mask >>= 1; 58 return (depth); 59 } 60 61 static int 62 efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt, 63 EFI_PIXEL_BITMASK *pixinfo) 64 { 65 int result; 66 67 result = 0; 68 switch (pixfmt) { 69 case PixelRedGreenBlueReserved8BitPerColor: 70 efifb->fb_mask_red = 0x000000ff; 71 efifb->fb_mask_green = 0x0000ff00; 72 efifb->fb_mask_blue = 0x00ff0000; 73 efifb->fb_mask_reserved = 0xff000000; 74 break; 75 case PixelBlueGreenRedReserved8BitPerColor: 76 efifb->fb_mask_red = 0x00ff0000; 77 efifb->fb_mask_green = 0x0000ff00; 78 efifb->fb_mask_blue = 0x000000ff; 79 efifb->fb_mask_reserved = 0xff000000; 80 break; 81 case PixelBitMask: 82 efifb->fb_mask_red = pixinfo->RedMask; 83 efifb->fb_mask_green = pixinfo->GreenMask; 84 efifb->fb_mask_blue = pixinfo->BlueMask; 85 efifb->fb_mask_reserved = pixinfo->ReservedMask; 86 break; 87 default: 88 result = 1; 89 break; 90 } 91 return (result); 92 } 93 94 static int 95 efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode, 96 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info) 97 { 98 int result; 99 100 efifb->fb_addr = mode->FrameBufferBase; 101 efifb->fb_size = mode->FrameBufferSize; 102 efifb->fb_height = info->VerticalResolution; 103 efifb->fb_width = info->HorizontalResolution; 104 efifb->fb_stride = info->PixelsPerScanLine; 105 result = efifb_mask_from_pixfmt(efifb, info->PixelFormat, 106 &info->PixelInformation); 107 return (result); 108 } 109 110 static ssize_t 111 efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line, 112 EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size) 113 { 114 EFI_UGA_PIXEL pix0, pix1; 115 uint8_t *data1, *data2; 116 size_t count, maxcount = 1024; 117 ssize_t ofs; 118 EFI_STATUS status; 119 u_int idx; 120 121 status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer, 122 0, line, 0, 0, 1, 1, 0); 123 if (EFI_ERROR(status)) { 124 printf("UGA BLT operation failed (video->buffer)"); 125 return (-1); 126 } 127 pix1.Red = ~pix0.Red; 128 pix1.Green = ~pix0.Green; 129 pix1.Blue = ~pix0.Blue; 130 pix1.Reserved = 0; 131 132 data1 = calloc(maxcount, 2); 133 if (data1 == NULL) { 134 printf("Unable to allocate memory"); 135 return (-1); 136 } 137 data2 = data1 + maxcount; 138 139 ofs = 0; 140 while (size > 0) { 141 count = min(size, maxcount); 142 143 status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, 144 EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, 145 data1); 146 if (EFI_ERROR(status)) { 147 printf("Error reading frame buffer (before)"); 148 goto fail; 149 } 150 status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo, 151 0, 0, 0, line, 1, 1, 0); 152 if (EFI_ERROR(status)) { 153 printf("UGA BLT operation failed (modify)"); 154 goto fail; 155 } 156 status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, 157 EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, 158 data2); 159 if (EFI_ERROR(status)) { 160 printf("Error reading frame buffer (after)"); 161 goto fail; 162 } 163 status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo, 164 0, 0, 0, line, 1, 1, 0); 165 if (EFI_ERROR(status)) { 166 printf("UGA BLT operation failed (restore)"); 167 goto fail; 168 } 169 for (idx = 0; idx < count; idx++) { 170 if (data1[idx] != data2[idx]) { 171 free(data1); 172 return (ofs + (idx & ~3)); 173 } 174 } 175 ofs += count; 176 size -= count; 177 } 178 printf("No change detected in frame buffer"); 179 180 fail: 181 printf(" -- error %llu\n", status); 182 free(data1); 183 return (-1); 184 } 185 186 static EFI_PCI_IO_PROTOCOL * 187 efifb_uga_get_pciio(void) 188 { 189 EFI_PCI_IO_PROTOCOL *pciio; 190 EFI_HANDLE *buf, *hp; 191 EFI_STATUS status; 192 UINTN bufsz; 193 194 /* Get all handles that support the UGA protocol. */ 195 bufsz = 0; 196 status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL); 197 if (status != EFI_BUFFER_TOO_SMALL) 198 return (NULL); 199 buf = malloc(bufsz); 200 status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf); 201 if (status != EFI_SUCCESS) { 202 free(buf); 203 return (NULL); 204 } 205 bufsz /= sizeof(EFI_HANDLE); 206 207 /* Get the PCI I/O interface of the first handle that supports it. */ 208 pciio = NULL; 209 for (hp = buf; hp < buf + bufsz; hp++) { 210 status = OpenProtocolByHandle(*hp, &pciio_guid, 211 (void **)&pciio); 212 if (status == EFI_SUCCESS) { 213 free(buf); 214 return (pciio); 215 } 216 } 217 free(buf); 218 return (NULL); 219 } 220 221 static EFI_STATUS 222 efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp, 223 uint64_t *sizep) 224 { 225 uint8_t *resattr; 226 uint64_t addr, size; 227 EFI_STATUS status; 228 u_int bar; 229 230 if (pciio == NULL) 231 return (EFI_DEVICE_ERROR); 232 233 /* Attempt to get the frame buffer address (imprecise). */ 234 *addrp = 0; 235 *sizep = 0; 236 for (bar = 0; bar < 6; bar++) { 237 status = pciio->GetBarAttributes(pciio, bar, NULL, 238 (void **)&resattr); 239 if (status != EFI_SUCCESS) 240 continue; 241 /* XXX magic offsets and constants. */ 242 if (resattr[0] == 0x87 && resattr[3] == 0) { 243 /* 32-bit address space descriptor (MEMIO) */ 244 addr = le32dec(resattr + 10); 245 size = le32dec(resattr + 22); 246 } else if (resattr[0] == 0x8a && resattr[3] == 0) { 247 /* 64-bit address space descriptor (MEMIO) */ 248 addr = le64dec(resattr + 14); 249 size = le64dec(resattr + 38); 250 } else { 251 addr = 0; 252 size = 0; 253 } 254 BS->FreePool(resattr); 255 if (addr == 0 || size == 0) 256 continue; 257 258 /* We assume the largest BAR is the frame buffer. */ 259 if (size > *sizep) { 260 *addrp = addr; 261 *sizep = size; 262 } 263 } 264 return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0); 265 } 266 267 static int 268 efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga) 269 { 270 EFI_PCI_IO_PROTOCOL *pciio; 271 char *ev, *p; 272 EFI_STATUS status; 273 ssize_t offset; 274 uint64_t fbaddr; 275 uint32_t horiz, vert, stride; 276 uint32_t np, depth, refresh; 277 278 status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh); 279 if (EFI_ERROR(status)) 280 return (1); 281 efifb->fb_height = vert; 282 efifb->fb_width = horiz; 283 /* Paranoia... */ 284 if (efifb->fb_height == 0 || efifb->fb_width == 0) 285 return (1); 286 287 /* The color masks are fixed AFAICT. */ 288 efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor, 289 NULL); 290 291 /* pciio can be NULL on return! */ 292 pciio = efifb_uga_get_pciio(); 293 294 /* Try to find the frame buffer. */ 295 status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr, 296 &efifb->fb_size); 297 if (EFI_ERROR(status)) { 298 efifb->fb_addr = 0; 299 efifb->fb_size = 0; 300 } 301 302 /* 303 * There's no reliable way to detect the frame buffer or the 304 * offset within the frame buffer of the visible region, nor 305 * the stride. Our only option is to look at the system and 306 * fill in the blanks based on that. Luckily, UGA was mostly 307 * only used on Apple hardware. 308 */ 309 offset = -1; 310 ev = getenv("smbios.system.maker"); 311 if (ev != NULL && !strcmp(ev, "Apple Inc.")) { 312 ev = getenv("smbios.system.product"); 313 if (ev != NULL && !strcmp(ev, "iMac7,1")) { 314 /* These are the expected values we should have. */ 315 horiz = 1680; 316 vert = 1050; 317 fbaddr = 0xc0000000; 318 /* These are the missing bits. */ 319 offset = 0x10000; 320 stride = 1728; 321 } else if (ev != NULL && !strcmp(ev, "MacBook3,1")) { 322 /* These are the expected values we should have. */ 323 horiz = 1280; 324 vert = 800; 325 fbaddr = 0xc0000000; 326 /* These are the missing bits. */ 327 offset = 0x0; 328 stride = 2048; 329 } 330 } 331 332 /* 333 * If this is hardware we know, make sure that it looks familiar 334 * before we accept our hardcoded values. 335 */ 336 if (offset >= 0 && efifb->fb_width == horiz && 337 efifb->fb_height == vert && efifb->fb_addr == fbaddr) { 338 efifb->fb_addr += offset; 339 efifb->fb_size -= offset; 340 efifb->fb_stride = stride; 341 return (0); 342 } else if (offset >= 0) { 343 printf("Hardware make/model known, but graphics not " 344 "as expected.\n"); 345 printf("Console may not work!\n"); 346 } 347 348 /* 349 * The stride is equal or larger to the width. Often it's the 350 * next larger power of two. We'll start with that... 351 */ 352 efifb->fb_stride = efifb->fb_width; 353 do { 354 np = efifb->fb_stride & (efifb->fb_stride - 1); 355 if (np) { 356 efifb->fb_stride |= (np - 1); 357 efifb->fb_stride++; 358 } 359 } while (np); 360 361 ev = getenv("hw.efifb.address"); 362 if (ev == NULL) { 363 if (efifb->fb_addr == 0) { 364 printf("Please set hw.efifb.address and " 365 "hw.efifb.stride.\n"); 366 return (1); 367 } 368 369 /* 370 * The visible part of the frame buffer may not start at 371 * offset 0, so try to detect it. Note that we may not 372 * always be able to read from the frame buffer, which 373 * means that we may not be able to detect anything. In 374 * that case, we would take a long time scanning for a 375 * pixel change in the frame buffer, which would have it 376 * appear that we're hanging, so we limit the scan to 377 * 1/256th of the frame buffer. This number is mostly 378 * based on PR 202730 and the fact that on a MacBoook, 379 * where we can't read from the frame buffer the offset 380 * of the visible region is 0. In short: we want to scan 381 * enough to handle all adapters that have an offset 382 * larger than 0 and we want to scan as little as we can 383 * to not appear to hang when we can't read from the 384 * frame buffer. 385 */ 386 offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr, 387 efifb->fb_size >> 8); 388 if (offset == -1) { 389 printf("Unable to reliably detect frame buffer.\n"); 390 } else if (offset > 0) { 391 efifb->fb_addr += offset; 392 efifb->fb_size -= offset; 393 } 394 } else { 395 offset = 0; 396 efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4; 397 efifb->fb_addr = strtoul(ev, &p, 0); 398 if (*p != '\0') 399 return (1); 400 } 401 402 ev = getenv("hw.efifb.stride"); 403 if (ev == NULL) { 404 if (pciio != NULL && offset != -1) { 405 /* Determine the stride. */ 406 offset = efifb_uga_find_pixel(uga, 1, pciio, 407 efifb->fb_addr, horiz * 8); 408 if (offset != -1) 409 efifb->fb_stride = offset >> 2; 410 } else { 411 printf("Unable to reliably detect the stride.\n"); 412 } 413 } else { 414 efifb->fb_stride = strtoul(ev, &p, 0); 415 if (*p != '\0') 416 return (1); 417 } 418 419 /* 420 * We finalized on the stride, so recalculate the size of the 421 * frame buffer. 422 */ 423 efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4; 424 return (0); 425 } 426 427 int 428 efi_find_framebuffer(struct efi_fb *efifb) 429 { 430 EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; 431 EFI_UGA_DRAW_PROTOCOL *uga; 432 EFI_STATUS status; 433 434 status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop); 435 if (status == EFI_SUCCESS) 436 return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info)); 437 438 status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga); 439 if (status == EFI_SUCCESS) 440 return (efifb_from_uga(efifb, uga)); 441 442 return (1); 443 } 444 445 static void 446 print_efifb(int mode, struct efi_fb *efifb, int verbose) 447 { 448 u_int depth; 449 450 if (mode >= 0) 451 printf("mode %d: ", mode); 452 depth = efi_framebuffer_bpp(efifb); 453 printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height, 454 depth, efifb->fb_stride); 455 if (verbose) { 456 printf("\n frame buffer: address=%jx, size=%jx", 457 (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size); 458 printf("\n color mask: R=%08x, G=%08x, B=%08x\n", 459 efifb->fb_mask_red, efifb->fb_mask_green, 460 efifb->fb_mask_blue); 461 } 462 } 463 464 COMMAND_SET(gop, "gop", "graphics output protocol", command_gop); 465 466 static int 467 command_gop(int argc, char *argv[]) 468 { 469 struct efi_fb efifb; 470 EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; 471 EFI_STATUS status; 472 u_int mode; 473 474 status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop); 475 if (EFI_ERROR(status)) { 476 snprintf(command_errbuf, sizeof(command_errbuf), 477 "%s: Graphics Output Protocol not present (error=%llu)", 478 argv[0], status); 479 return (CMD_ERROR); 480 } 481 482 if (argc < 2) 483 goto usage; 484 485 if (!strcmp(argv[1], "set")) { 486 char *cp; 487 488 if (argc != 3) 489 goto usage; 490 mode = strtol(argv[2], &cp, 0); 491 if (cp[0] != '\0') { 492 sprintf(command_errbuf, "mode is an integer"); 493 return (CMD_ERROR); 494 } 495 status = gop->SetMode(gop, mode); 496 if (EFI_ERROR(status)) { 497 snprintf(command_errbuf, sizeof(command_errbuf), 498 "%s: Unable to set mode to %u (error=%llu)", 499 argv[0], mode, status); 500 return (CMD_ERROR); 501 } 502 } else if (!strcmp(argv[1], "get")) { 503 if (argc != 2) 504 goto usage; 505 efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info); 506 print_efifb(gop->Mode->Mode, &efifb, 1); 507 printf("\n"); 508 } else if (!strcmp(argv[1], "list")) { 509 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; 510 UINTN infosz; 511 512 if (argc != 2) 513 goto usage; 514 pager_open(); 515 for (mode = 0; mode < gop->Mode->MaxMode; mode++) { 516 status = gop->QueryMode(gop, mode, &infosz, &info); 517 if (EFI_ERROR(status)) 518 continue; 519 efifb_from_gop(&efifb, gop->Mode, info); 520 print_efifb(mode, &efifb, 0); 521 if (pager_output("\n")) 522 break; 523 } 524 pager_close(); 525 } 526 return (CMD_OK); 527 528 usage: 529 snprintf(command_errbuf, sizeof(command_errbuf), 530 "usage: %s [list | get | set <mode>]", argv[0]); 531 return (CMD_ERROR); 532 } 533 534 COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga); 535 536 static int 537 command_uga(int argc, char *argv[]) 538 { 539 struct efi_fb efifb; 540 EFI_UGA_DRAW_PROTOCOL *uga; 541 EFI_STATUS status; 542 543 status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga); 544 if (EFI_ERROR(status)) { 545 snprintf(command_errbuf, sizeof(command_errbuf), 546 "%s: UGA Protocol not present (error=%llu)", 547 argv[0], status); 548 return (CMD_ERROR); 549 } 550 551 if (argc != 1) 552 goto usage; 553 554 if (efifb_from_uga(&efifb, uga) != CMD_OK) { 555 snprintf(command_errbuf, sizeof(command_errbuf), 556 "%s: Unable to get UGA information", argv[0]); 557 return (CMD_ERROR); 558 } 559 560 print_efifb(-1, &efifb, 1); 561 printf("\n"); 562 return (CMD_OK); 563 564 usage: 565 snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]); 566 return (CMD_ERROR); 567 } 568