[coreboot-gerrit] New patch to review for coreboot: 59168cb drivers/realtek: Add driver stub to correct buggy firmware with r8168

Damien Zammit (damien@zamaudio.com) gerrit at coreboot.org
Mon Apr 20 08:56:30 CEST 2015


Damien Zammit (damien at zamaudio.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9806

-gerrit

commit 59168cb9b0d3ed14f4980b2b65ed5033789eb9f5
Author: Damien Zammit <damien at zamaudio.com>
Date:   Mon Apr 20 16:49:00 2015 +1000

    drivers/realtek: Add driver stub to correct buggy firmware with r8168
    
    Some r8168 network cards return prefetchable memory on their internel addresses.
    Add infrastructure and option to force non-prefetchable memory on allocation.
    
    Change-Id: I07512494791de1ad8659521617b7a1661e4da0d3
    Signed-off-by: Damien Zammit <damien at zamaudio.com>
---
 src/device/device.c          |   8 +--
 src/device/pci_device.c      | 141 +++++++++++++++++++++++++++++++++++++++++++
 src/drivers/net/Kconfig      |   5 ++
 src/drivers/net/Makefile.inc |   1 +
 src/drivers/net/realtek.c    |  29 +++++++++
 src/include/device/pci.h     |   2 +
 6 files changed, 182 insertions(+), 4 deletions(-)

diff --git a/src/device/device.c b/src/device/device.c
index b3b8d24..0c2498b 100644
--- a/src/device/device.c
+++ b/src/device/device.c
@@ -704,8 +704,8 @@ static void avoid_fixed_resources(struct device *dev)
 	for (res = dev->resource_list; res; res = res->next) {
 		if ((res->flags & IORESOURCE_FIXED))
 			continue;
-		printk(BIOS_SPEW, "%s:@%s %02lx limit %08llx\n", __func__,
-		       dev_path(dev), res->index, res->limit);
+		printk(BIOS_SPEW, "%s: %s@%08llx limit %08llx\n", __func__,
+		       dev_path(dev), res->base, res->limit);
 		if ((res->flags & MEM_MASK) == PREF_TYPE &&
 		    (res->limit < limits.pref.limit))
 			limits.pref.limit = res->limit;
@@ -737,8 +737,8 @@ static void avoid_fixed_resources(struct device *dev)
 		else
 			continue;
 
-		printk(BIOS_SPEW, "%s2: %s@%02lx limit %08llx\n", __func__,
-			     dev_path(dev), res->index, res->limit);
+		printk(BIOS_SPEW, "%s2: %s@%08llx limit %08llx\n", __func__,
+			     dev_path(dev), res->base, res->limit);
 		printk(BIOS_SPEW, "\tlim->base %08llx lim->limit %08llx\n",
 			     lim->base, lim->limit);
 
diff --git a/src/device/pci_device.c b/src/device/pci_device.c
index 4651258..74cc63f 100644
--- a/src/device/pci_device.c
+++ b/src/device/pci_device.c
@@ -163,6 +163,120 @@ unsigned pci_find_capability(device_t dev, unsigned cap)
 
 /**
  * Given a device and register, read the size of the BAR for that register.
+ * Hardcode any prefetch mem flags to mem (call only for buggy devices).
+ *
+ * @param dev Pointer to the device structure.
+ * @param index Address of the PCI configuration register.
+ * @return TODO
+ */
+struct resource *pci_get_resource_no_prefetch_mem(struct device *dev, unsigned long index)
+{
+	struct resource *resource;
+	unsigned long value, attr;
+	resource_t moving, limit;
+
+	/* Initialize the resources to nothing. */
+	resource = new_resource(dev, index);
+
+	/* Get the initial value. */
+	value = pci_read_config32(dev, index);
+
+	/* See which bits move. */
+	moving = pci_moving_config32(dev, index);
+
+	/* Initialize attr to the bits that do not move. */
+	attr = value & ~moving;
+
+	/* If it is a 64bit resource look at the high half as well. */
+	if (((attr & PCI_BASE_ADDRESS_SPACE_IO) == 0) &&
+	    ((attr & PCI_BASE_ADDRESS_MEM_LIMIT_MASK) ==
+	     PCI_BASE_ADDRESS_MEM_LIMIT_64)) {
+		/* Find the high bits that move. */
+		moving |=
+		    ((resource_t) pci_moving_config32(dev, index + 4)) << 32;
+	}
+
+	/* Find the resource constraints.
+	 * Start by finding the bits that move. From there:
+	 * - Size is the least significant bit of the bits that move.
+	 * - Limit is all of the bits that move plus all of the lower bits.
+	 * See PCI Spec 6.2.5.1.
+	 */
+	limit = 0;
+	if (moving) {
+		resource->size = 1;
+		resource->align = resource->gran = 0;
+		while (!(moving & resource->size)) {
+			resource->size <<= 1;
+			resource->align += 1;
+			resource->gran += 1;
+		}
+		resource->limit = limit = moving | (resource->size - 1);
+	}
+
+	/*
+	 * Some broken hardware has read-only registers that do not
+	 * really size correctly.
+	 *
+	 * Example: the Acer M7229 has BARs 1-4 normally read-only,
+	 * so BAR1 at offset 0x10 reads 0x1f1. If you size that register
+	 * by writing 0xffffffff to it, it will read back as 0x1f1 -- which
+	 * is a violation of the spec.
+	 *
+	 * We catch this case and ignore it by observing which bits move.
+	 *
+	 * This also catches the common case of unimplemented registers
+	 * that always read back as 0.
+	 */
+	if (moving == 0) {
+		if (value != 0) {
+			printk(BIOS_DEBUG, "%s register %02lx(%08lx), "
+			       "read-only ignoring it\n",
+			       dev_path(dev), index, value);
+		}
+		resource->flags = 0;
+	} else if (attr & PCI_BASE_ADDRESS_SPACE_IO) {
+		/* An I/O mapped base address. */
+		attr &= PCI_BASE_ADDRESS_IO_ATTR_MASK;
+		resource->flags |= IORESOURCE_IO;
+		/* I don't want to deal with 32bit I/O resources. */
+		resource->limit = 0xffff;
+	} else {
+		/* A Memory mapped base address. */
+		attr &= PCI_BASE_ADDRESS_MEM_ATTR_MASK;
+		resource->flags |= IORESOURCE_MEM;
+		// Ignore prefetch
+		//if (attr & PCI_BASE_ADDRESS_MEM_PREFETCH)
+		//	resource->flags |= IORESOURCE_PREFETCH;
+		attr &= PCI_BASE_ADDRESS_MEM_LIMIT_MASK;
+		if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_32) {
+			/* 32bit limit. */
+			resource->limit = 0xffffffffUL;
+		} else if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_1M) {
+			/* 1MB limit. */
+			resource->limit = 0x000fffffUL;
+		} else if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_64) {
+			/* 64bit limit. */
+			resource->limit = 0xffffffffffffffffULL;
+			resource->flags |= IORESOURCE_PCI64;
+		} else {
+			/* Invalid value. */
+			printk(BIOS_ERR, "Broken BAR with value %lx\n", attr);
+			printk(BIOS_ERR, " on dev %s at index %02lx\n",
+			       dev_path(dev), index);
+			resource->flags = 0;
+		}
+	}
+
+	/* Don't let the limit exceed which bits can move. */
+	if (resource->limit > limit)
+		resource->limit = limit;
+
+	return resource;
+}
+
+/**
+ * Given a device and register, read the size of the BAR for that register.
  *
  * @param dev Pointer to the device structure.
  * @param index Address of the PCI configuration register.
@@ -325,6 +439,27 @@ static void pci_get_rom_resource(struct device *dev, unsigned long index)
 }
 
 /**
+ * Read the base address registers for a given device,
+ * but choose mem over prefetch mem for buggy devices.
+ *
+ * @param dev Pointer to the dev structure.
+ * @param howmany How many registers to read (6 for device, 2 for bridge).
+ */
+static void pci_read_bases_no_prefetch_mem(struct device *dev, unsigned int howmany)
+{
+	unsigned long index;
+
+	for (index = PCI_BASE_ADDRESS_0;
+	     (index < PCI_BASE_ADDRESS_0 + (howmany << 2));) {
+		struct resource *resource;
+		resource = pci_get_resource_no_prefetch_mem(dev, index);
+		index += (resource->flags & IORESOURCE_PCI64) ? 8 : 4;
+	}
+
+	compact_resources(dev);
+}
+
+/**
  * Read the base address registers for a given device.
  *
  * @param dev Pointer to the dev structure.
@@ -425,6 +560,12 @@ void pci_dev_read_resources(struct device *dev)
 	pci_get_rom_resource(dev, PCI_ROM_ADDRESS);
 }
 
+void pci_dev_read_resources_no_prefetch(struct device *dev)
+{
+	pci_read_bases_no_prefetch_mem(dev, 6);
+	pci_get_rom_resource(dev, PCI_ROM_ADDRESS);
+}
+
 void pci_bus_read_resources(struct device *dev)
 {
 	pci_bridge_read_bases(dev);
diff --git a/src/drivers/net/Kconfig b/src/drivers/net/Kconfig
new file mode 100644
index 0000000..f1631fd
--- /dev/null
+++ b/src/drivers/net/Kconfig
@@ -0,0 +1,5 @@
+config REALTEK_8168_FIX
+	bool "Realtek 8168 fixup"
+	help
+	 Select this when you have a buggy realtek 8168 card
+	 that thinks its memory is prefetchable.
diff --git a/src/drivers/net/Makefile.inc b/src/drivers/net/Makefile.inc
index 9b3008d..f6fd803 100644
--- a/src/drivers/net/Makefile.inc
+++ b/src/drivers/net/Makefile.inc
@@ -1,2 +1,3 @@
 romstage-$(CONFIG_CONSOLE_NE2K) += ne2k.c
 ramstage-$(CONFIG_CONSOLE_NE2K) += ne2k.c
+ramstage-$(CONFIG_REALTEK_8168_FIX) += realtek.c
diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c
new file mode 100644
index 0000000..78d55a7
--- /dev/null
+++ b/src/drivers/net/realtek.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015  Damien Zammit <damien at zamaudio.com>
+ *
+ * This driver simply forces the 10ec:8168 device not to use prefetchable
+ * memory addresses on buggy hardware, which fixes an issue with the pci
+ * allocator
+ */
+
+#include <arch/io.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct device_operations r8168bug_ops  = {
+	.read_resources   = pci_dev_read_resources_no_prefetch,
+	.set_resources    = pci_dev_set_resources,
+	.enable_resources = pci_dev_enable_resources,
+	.init             = 0,
+	.scan_bus         = 0,
+};
+
+static const struct pci_driver r8168bug_driver __pci_driver = {
+        .ops    = &r8168bug_ops,
+        .vendor = 0x10ec,
+        .device = 0x8168,
+};
diff --git a/src/include/device/pci.h b/src/include/device/pci.h
index 4e712f9..27b3327 100644
--- a/src/include/device/pci.h
+++ b/src/include/device/pci.h
@@ -63,6 +63,7 @@ extern struct pci_driver epci_drivers[];
 extern struct device_operations default_pci_ops_dev;
 extern struct device_operations default_pci_ops_bus;
 
+void pci_dev_read_resources_no_prefetch(device_t dev);
 void pci_dev_read_resources(device_t dev);
 void pci_bus_read_resources(device_t dev);
 void pci_dev_set_resources(device_t dev);
@@ -79,6 +80,7 @@ uint8_t pci_moving_config8(struct device *dev, unsigned reg);
 uint16_t pci_moving_config16(struct device *dev, unsigned reg);
 uint32_t pci_moving_config32(struct device *dev, unsigned reg);
 struct resource *pci_get_resource(struct device *dev, unsigned long index);
+struct resource *pci_get_resource_no_prefetch_mem(struct device *dev, unsigned long index);
 void pci_dev_set_subsystem(device_t dev, unsigned vendor, unsigned device);
 void pci_dev_init(struct device *dev);
 unsigned int pci_match_simple_dev(device_t dev, pci_devfn_t sdev);



More information about the coreboot-gerrit mailing list