Skip site navigation (1)Skip section navigation (2)
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>