Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 5 Sep 2015 18:24:52 +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: r287489 - head/sys/boot/efi/loader/arch/amd64
Message-ID:  <201509051824.t85IOqYZ088561@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: marcel
Date: Sat Sep  5 18:24:51 2015
New Revision: 287489
URL: https://svnweb.freebsd.org/changeset/base/287489

Log:
  Auto-detect the UGA frame buffer and stride on a MacBook. We're
  striking a delicate balance between exhaustive searching and
  banking on assumptions. The environment variables can be used
  as a fall-back anyway. With this change, all known and tested
  Macs with only UGA should have a working console out of the
  box... for now...

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	Sat Sep  5 17:34:49 2015	(r287488)
+++ head/sys/boot/efi/loader/arch/amd64/framebuffer.c	Sat Sep  5 18:24:51 2015	(r287489)
@@ -175,7 +175,7 @@ efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOC
 		ofs += count;
 		size -= count;
 	}
-	printf("Couldn't find the pixel");
+	printf("No change detected in frame buffer");
 
  fail:
 	printf(" -- error %lu\n", status & ~EFI_ERROR_MASK);
@@ -218,18 +218,20 @@ efifb_uga_get_pciio(void)
 }
 
 static EFI_STATUS
-efifb_uga_detect_framebuffer(EFI_UGA_DRAW_PROTOCOL *uga,
-    EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp, uint64_t *sizep)
+efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp,
+    uint64_t *sizep)
 {
 	uint8_t *resattr;
-	uint64_t a, addr, s, size;
-	ssize_t ofs;
+	uint64_t addr, size;
 	EFI_STATUS status;
 	u_int bar;
 
+	if (pciio == NULL)
+		return (EFI_DEVICE_ERROR);
+
 	/* Attempt to get the frame buffer address (imprecise). */
-	addr = 0;
-	size = 0;
+	*addrp = 0;
+	*sizep = 0;
 	for (bar = 0; bar < 6; bar++) {
 		status = pciio->GetBarAttributes(pciio, bar, NULL,
 		    (void **)&resattr);
@@ -238,43 +240,27 @@ efifb_uga_detect_framebuffer(EFI_UGA_DRA
 		/* XXX magic offsets and constants. */
 		if (resattr[0] == 0x87 && resattr[3] == 0) {
 			/* 32-bit address space descriptor (MEMIO) */
-			a = le32dec(resattr + 10);
-			s = le32dec(resattr + 22);
+			addr = le32dec(resattr + 10);
+			size = le32dec(resattr + 22);
 		} else if (resattr[0] == 0x8a && resattr[3] == 0) {
 			/* 64-bit address space descriptor (MEMIO) */
-			a = le64dec(resattr + 14);
-			s = le64dec(resattr + 38);
+			addr = le64dec(resattr + 14);
+			size = le64dec(resattr + 38);
 		} else {
-			a = 0;
-			s = 0;
+			addr = 0;
+			size = 0;
 		}
 		BS->FreePool(resattr);
-		if (a == 0 || s == 0)
+		if (addr == 0 || size == 0)
 			continue;
 
 		/* We assume the largest BAR is the frame buffer. */
-		if (s > size) {
-			addr = a;
-			size = s;
+		if (size > *sizep) {
+			*addrp = addr;
+			*sizep = size;
 		}
 	}
-	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;
-
-	*addrp = addr;
-	*sizep = size;
-	return (0);
+	return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0);
 }
 
 static int
@@ -284,29 +270,75 @@ efifb_from_uga(struct efi_fb *efifb, EFI
 	char *ev, *p;
 	EFI_STATUS status;
 	ssize_t ofs;
-	uint32_t horiz, vert, depth, refresh;
+	uint32_t np, 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;
+	/* Paranoia... */
+	if (efifb->fb_height == 0 || efifb->fb_width == 0)
+		return (1);
+
+	/* The color masks are fixed AFAICT. */
 	efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor,
 	    NULL);
 
+	/*
+	 * The stride is equal or larger to the width. Often it's the
+	 * next larger power of two. We'll start with that...
+	 */
+	efifb->fb_stride = efifb->fb_width;
+	do {
+		np = efifb->fb_stride & (efifb->fb_stride - 1);
+		if (np) {
+			efifb->fb_stride |= (np - 1);
+			efifb->fb_stride++;
+		}
+	} while (np);
+
+	/* pciio can be NULL on return! */
 	pciio = efifb_uga_get_pciio();
-	if (pciio == NULL)
-		return (1);
 
 	ev = getenv("uga_framebuffer");
 	if (ev == NULL) {
 		/* Try to find the frame buffer. */
-		status = efifb_uga_detect_framebuffer(uga, pciio,
-		    &efifb->fb_addr, &efifb->fb_size);
-		if (EFI_ERROR(status))
+		status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr,
+		    &efifb->fb_size);
+		if (EFI_ERROR(status)) {
+			printf("Please set uga_framebuffer!\n");
 			return (1);
+		}
+
+		/*
+		 * The visible part of the frame buffer may not start at
+		 * offset 0, so try to detect it. Note that we may not
+		 * always be able to read from the frame buffer, which
+		 * means that we may not be able to detect anything. In
+		 * that case, we would take a long time scanning for a
+		 * pixel change in the frame buffer, which would have it
+		 * appear that we're hanging, so we limit the scan to
+		 * 1/256th of the frame buffer. This number is mostly
+		 * based on PR 202730 and the fact that on a MacBoook,
+		 * where we can't read from the frame buffer the offset
+		 * of the visible region is 0. In short: we want to scan
+		 * enough to handle all adapters that have an offset
+		 * larger than 0 and we want to scan as little as we can
+		 * to not appear to hang when we can't read from the
+		 * frame buffer.
+		 */
+		ofs = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr,
+		    efifb->fb_size >> 8);
+		if (ofs == -1) {
+			printf("Unable to reliably detect frame buffer.\n");
+		} else if (ofs > 0) {
+			efifb->fb_addr += ofs;
+			efifb->fb_size -= ofs;
+		}
 	} else {
-		efifb->fb_size = horiz * vert * 4;
+		ofs = 0;
+		efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
 		efifb->fb_addr = strtoul(ev, &p, 0);
 		if (*p != '\0')
 			return (1);
@@ -314,17 +346,26 @@ efifb_from_uga(struct efi_fb *efifb, EFI
 
 	ev = getenv("uga_stride");
 	if (ev == NULL) {
-		/* Try to detect the stride. */
-		ofs = efifb_uga_find_pixel(uga, 1, pciio, efifb->fb_addr,
-		    efifb->fb_size);
-		if (ofs == -1)
-			return (1);
-		efifb->fb_stride = ofs >> 2;
+		if (pciio != NULL && ofs != -1) {
+			/* Determine the stride. */
+			ofs = efifb_uga_find_pixel(uga, 1, pciio,
+			    efifb->fb_addr, horiz * 8);
+			if (ofs != -1)
+				efifb->fb_stride = ofs >> 2;
+		} else {
+			printf("Unable to reliably detect the stride.\n");
+		}
 	} else {
 		efifb->fb_stride = strtoul(ev, &p, 0);
 		if (*p != '\0')
 			return (1);
 	}
+
+	/*
+	 * We finalized on the stride, so recalculate the size of the
+	 * frame buffer.
+	 */
+	efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
 	return (0);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201509051824.t85IOqYZ088561>