Date: Thu, 3 Sep 2015 04:35:18 +0000 (UTC) From: Marcel Moolenaar <marcel@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r287422 - head/sys/boot/efi/loader/arch/amd64 Message-ID: <201509030435.t834ZI7i026261@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: marcel Date: Thu Sep 3 04:35:17 2015 New Revision: 287422 URL: https://svnweb.freebsd.org/changeset/base/287422 Log: For UGA, the frame buffer address obtained by scanning the PCI BARs does not necessarily correspond to the upper-left most pixel. Scan the frame buffer for which byte changed when changing the pixel at (0,0). Use the same technique to determine the stride. Except for changing the pixel at (0,0), we change the pixel at (0,1). PR: 202730 Tested by: hartzell (at) alerce.com Modified: head/sys/boot/efi/loader/arch/amd64/framebuffer.c Modified: head/sys/boot/efi/loader/arch/amd64/framebuffer.c ============================================================================== --- head/sys/boot/efi/loader/arch/amd64/framebuffer.c Thu Sep 3 03:58:59 2015 (r287421) +++ head/sys/boot/efi/loader/arch/amd64/framebuffer.c Thu Sep 3 04:35:17 2015 (r287422) @@ -43,6 +43,21 @@ static EFI_GUID gop_guid = EFI_GRAPHICS_ static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID; static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID; +static u_int +efifb_color_depth(struct efi_fb *efifb) +{ + uint32_t mask; + u_int depth; + + mask = efifb->fb_mask_red | efifb->fb_mask_green | + efifb->fb_mask_blue | efifb->fb_mask_reserved; + if (mask == 0) + return (0); + for (depth = 1; mask != 1; depth++) + mask >>= 1; + return (depth); +} + static int efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt, EFI_PIXEL_BITMASK *pixinfo) @@ -92,83 +107,198 @@ efifb_from_gop(struct efi_fb *efifb, EFI return (result); } -static int -efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga) +static ssize_t +efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line, + EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size) +{ + EFI_UGA_PIXEL pix0, pix1; + uint8_t *data1, *data2; + size_t count, maxcount = 1024; + ssize_t ofs; + EFI_STATUS status; + u_int idx; + + status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer, + 0, line, 0, 0, 1, 1, 0); + if (EFI_ERROR(status)) { + printf("UGA BLT operation failed (video->buffer)"); + return (-1); + } + pix1.Red = ~pix0.Red; + pix1.Green = ~pix0.Green; + pix1.Blue = ~pix0.Blue; + pix1.Reserved = 0; + + data1 = calloc(maxcount, 2); + if (data1 == NULL) { + printf("Unable to allocate memory"); + return (-1); + } + data2 = data1 + maxcount; + + ofs = 0; + while (size > 0) { + count = min(size, maxcount); + + status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, + EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, + data1); + if (EFI_ERROR(status)) { + printf("Error reading frame buffer (before)"); + goto fail; + } + status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo, + 0, 0, 0, line, 1, 1, 0); + if (EFI_ERROR(status)) { + printf("UGA BLT operation failed (modify)"); + goto fail; + } + status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32, + EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2, + data2); + if (EFI_ERROR(status)) { + printf("Error reading frame buffer (after)"); + goto fail; + } + status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo, + 0, 0, 0, line, 1, 1, 0); + if (EFI_ERROR(status)) { + printf("UGA BLT operation failed (restore)"); + goto fail; + } + for (idx = 0; idx < count; idx++) { + if (data1[idx] != data2[idx]) { + free(data1); + return (ofs + (idx & ~3)); + } + } + ofs += count; + size -= count; + } + printf("Couldn't find the pixel"); + + fail: + printf(" -- error %lu\n", status & ~EFI_ERROR_MASK); + free(data1); + return (-1); +} + +static EFI_STATUS +efifb_uga_detect_framebuffer(EFI_UGA_DRAW_PROTOCOL *uga, + EFI_PCI_IO_PROTOCOL **pciiop, uint64_t *addrp, uint64_t *sizep) { - uint8_t *buf; EFI_PCI_IO_PROTOCOL *pciio; - EFI_HANDLE handle; + EFI_HANDLE *buf, *hp; + uint8_t *resattr; + uint64_t a, addr, s, size; + ssize_t ofs; EFI_STATUS status; - UINTN bufofs, bufsz; - uint64_t address, length; - uint32_t horiz, vert, depth, refresh; + UINTN bufsz; u_int bar; - status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh); - if (EFI_ERROR(status)) - return (1); - efifb->fb_height = vert; - efifb->fb_width = horiz; - efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor, - NULL); - /* Find all handles that support the UGA protocol. */ + /* Get all handles that support the UGA protocol. */ bufsz = 0; status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL); if (status != EFI_BUFFER_TOO_SMALL) - return (1); + return (status); buf = malloc(bufsz); - status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, - (EFI_HANDLE *)buf); + status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf); if (status != EFI_SUCCESS) { free(buf); - return (1); + return (status); } + bufsz /= sizeof(EFI_HANDLE); + /* Get the PCI I/O interface of the first handle that supports it. */ pciio = NULL; - for (bufofs = 0; bufofs < bufsz; bufofs += sizeof(EFI_HANDLE)) { - handle = *(EFI_HANDLE *)(buf + bufofs); - status = BS->HandleProtocol(handle, &pciio_guid, - (void **)&pciio); + for (hp = buf; hp < buf + bufsz; hp++) { + status = BS->HandleProtocol(*hp, &pciio_guid, (void **)&pciio); if (status == EFI_SUCCESS) break; } free(buf); - if (pciio == NULL) - return (1); + if (status != EFI_SUCCESS || pciio == NULL) + return (EFI_NOT_FOUND); + /* Attempt to get the frame buffer address (imprecise). */ - efifb->fb_addr = 0; - efifb->fb_size = 0; + addr = 0; + size = 0; for (bar = 0; bar < 6; bar++) { status = pciio->GetBarAttributes(pciio, bar, NULL, - (EFI_HANDLE *)&buf); + (void **)&resattr); if (status != EFI_SUCCESS) continue; /* XXX magic offsets and constants. */ - if (buf[0] == 0x87 && buf[3] == 0) { + if (resattr[0] == 0x87 && resattr[3] == 0) { /* 32-bit address space descriptor (MEMIO) */ - address = le32dec(buf + 10); - length = le32dec(buf + 22); - } else if (buf[0] == 0x8a && buf[3] == 0) { + a = le32dec(resattr + 10); + s = le32dec(resattr + 22); + } else if (resattr[0] == 0x8a && resattr[3] == 0) { /* 64-bit address space descriptor (MEMIO) */ - address = le64dec(buf + 14); - length = le64dec(buf + 38); + a = le64dec(resattr + 14); + s = le64dec(resattr + 38); } else { - address = 0; - length = 0; + a = 0; + s = 0; } - BS->FreePool(buf); - if (address == 0 || length == 0) + BS->FreePool(resattr); + if (a == 0 || s == 0) continue; + /* We assume the largest BAR is the frame buffer. */ - if (length > efifb->fb_size) { - efifb->fb_addr = address; - efifb->fb_size = length; + if (s > size) { + addr = a; + size = s; } } - if (efifb->fb_addr == 0 || efifb->fb_size == 0) + if (addr == 0 || size == 0) + return (EFI_DEVICE_ERROR); + + /* + * The visible part of the frame buffer may not start at offset + * 0, so try to detect it. + */ + ofs = efifb_uga_find_pixel(uga, 0, pciio, addr, size); + if (ofs == -1) + return (EFI_NO_RESPONSE); + + addr += ofs; + size -= ofs; + + *pciiop = pciio; + *addrp = addr; + *sizep = size; + return (0); +} + +static int +efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga) +{ + EFI_PCI_IO_PROTOCOL *pciio; + EFI_STATUS status; + ssize_t ofs; + uint32_t horiz, vert, depth, refresh; + + status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh); + if (EFI_ERROR(status)) + return (1); + efifb->fb_height = vert; + efifb->fb_width = horiz; + efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor, + NULL); + + /* Try and find the frame buffer. */ + status = efifb_uga_detect_framebuffer(uga, &pciio, &efifb->fb_addr, + &efifb->fb_size); + if (EFI_ERROR(status)) + return (1); + + /* Try and detect the stride. */ + ofs = efifb_uga_find_pixel(uga, 1, pciio, efifb->fb_addr, + efifb->fb_size); + if (ofs == -1) return (1); - /* TODO determine the stride. */ - efifb->fb_stride = efifb->fb_width; /* XXX */ + efifb->fb_stride = ofs >> 2; return (0); } @@ -193,18 +323,11 @@ efi_find_framebuffer(struct efi_fb *efif static void print_efifb(int mode, struct efi_fb *efifb, int verbose) { - uint32_t mask; u_int depth; if (mode >= 0) printf("mode %d: ", mode); - mask = efifb->fb_mask_red | efifb->fb_mask_green | - efifb->fb_mask_blue | efifb->fb_mask_reserved; - if (mask > 0) { - for (depth = 1; mask != 1; depth++) - mask >>= 1; - } else - depth = 0; + depth = efifb_color_depth(efifb); printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height, depth, efifb->fb_stride); if (verbose) {
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201509030435.t834ZI7i026261>