Index: sys/boot/efi/loader/arch/amd64/framebuffer.c =================================================================== --- sys/boot/efi/loader/arch/amd64/framebuffer.c (revision 287317) +++ sys/boot/efi/loader/arch/amd64/framebuffer.c (working copy) @@ -43,6 +43,21 @@ 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,10 +107,35 @@ return (result); } +static u_int +match_scanline(uint8_t *match, EFI_UGA_PIXEL *pix, uint8_t *buf, u_int bpp, + struct efi_fb *efifb) +{ + u_int nmatches, index, offset; + + nmatches = 0; + for (index = 0; index < efifb->fb_width; index++) { + if (!match[index]) + continue; + match[index] = 0; + offset = index * bpp; + if (buf[offset + ffs(efifb->fb_mask_red) / 8] != pix->Red) + continue; + if (buf[offset + ffs(efifb->fb_mask_green) / 8] != pix->Green) + continue; + if (buf[offset + ffs(efifb->fb_mask_blue) / 8] != pix->Blue) + continue; + match[index] = 1; + nmatches++; + } + return (nmatches); +} + static int efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga) { - uint8_t *buf; + EFI_UGA_PIXEL pix0, pix1; + uint8_t *buf, *match; EFI_PCI_IO_PROTOCOL *pciio; EFI_HANDLE handle; EFI_STATUS status; @@ -102,7 +142,7 @@ UINTN bufofs, bufsz; uint64_t address, length; uint32_t horiz, vert, depth, refresh; - u_int bar; + u_int bar, bpp, fbbar, idx, nmatches; status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh); if (EFI_ERROR(status)) @@ -138,6 +178,7 @@ /* Attempt to get the frame buffer address (imprecise). */ efifb->fb_addr = 0; efifb->fb_size = 0; + fbbar = -1; for (bar = 0; bar < 6; bar++) { status = pciio->GetBarAttributes(pciio, bar, NULL, (EFI_HANDLE *)&buf); @@ -163,12 +204,49 @@ if (length > efifb->fb_size) { efifb->fb_addr = address; efifb->fb_size = length; + fbbar = bar; } } if (efifb->fb_addr == 0 || efifb->fb_size == 0) return (1); - /* TODO determine the stride. */ - efifb->fb_stride = efifb->fb_width; /* XXX */ + /* + * Determine the stride by changing the first pixel on the second + * scan line and looking for the offset in the frame buffer that + * matches the new value. + */ + bpp = (efifb_color_depth(efifb) + 7) >> 3; + address = length = efifb->fb_width * bpp; + buf = malloc(length); + match = malloc(efifb->fb_width); + memset(match, 1, efifb->fb_width); + nmatches = efifb->fb_width; + uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer, 0, 1, 0, 0, 1, 1, 0); + pciio->Mem.Read(pciio, EfiPciIoWidthUint8, fbbar, address, + length, buf); + nmatches = match_scanline(match, &pix0, buf, bpp, efifb); + if (nmatches > 1) { + pix1.Red = ~pix0.Red; + pix1.Green = ~pix0.Green; + pix1.Blue = ~pix0.Blue; + uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo, 0, 0, + 0, 1, 1, 1, 0); + pciio->Mem.Read(pciio, EfiPciIoWidthUint8, fbbar, address, + length, buf); + nmatches = match_scanline(match, &pix1, buf, bpp, efifb); + } + uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo, 0, 0, 0, 1, 1, 1, 0); + free(buf); + printf("Found %u locations\n", nmatches); + if (nmatches != 1) { + free(match); + return (1); + } + for (idx = 0; idx < efifb->fb_width; idx++) { + if (match[idx]) + break; + } + efifb->fb_stride = efifb->fb_width + idx; + printf("stride = %u\n", efifb->fb_stride); return (0); } @@ -193,18 +271,11 @@ 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) {