Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 9 May 2013 19:24:50 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r250418 - head/sys/dev/pci
Message-ID:  <201305091924.r49JOo7E004649@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jhb
Date: Thu May  9 19:24:50 2013
New Revision: 250418
URL: http://svnweb.freebsd.org/changeset/base/250418

Log:
  Revision 233677 broke certain machines.  Specifically, if the firmware/BIOS
  assigned conflicting ranges to BARs then leaving the BARs alone could
  result in one device stealing mmio accesses intended to go to a second
  device.  Prior to 233677 the PCI bus driver attempted to handle this case
  by clearing the BAR to 0 depending on BARs based at 0 not decoding (which
  is not guaranteed to be true).  Now when a conflicting BAR is detected the
  following steps are taken:
  
   1) If hw.pci.realloc_bars (a new tunable) is enabled (default is enabled),
      then ignore the current BAR setting from the firmware and attempt to
      allocate a fresh resource range for the BAR.
  
   2) If 1) failed (or was disabled), disable decoding for the relevant
      BAR type (e.g. disable mem decoding for a memory BAR) and emit a
      warning if booting verbose.
  
  Tested by:	Alex Keda <admin@lissyara.su>
  MFC after:	1 week

Modified:
  head/sys/dev/pci/pci.c

Modified: head/sys/dev/pci/pci.c
==============================================================================
--- head/sys/dev/pci/pci.c	Thu May  9 18:43:01 2013	(r250417)
+++ head/sys/dev/pci/pci.c	Thu May  9 19:24:50 2013	(r250418)
@@ -280,6 +280,12 @@ SYSCTL_INT(_hw_pci, OID_AUTO, enable_io_
 enable these bits correctly.  We'd like to do this all the time, but there\n\
 are some peripherals that this causes problems with.");
 
+static int pci_do_realloc_bars = 1;
+TUNABLE_INT("hw.pci.realloc_bars", &pci_do_realloc_bars);
+SYSCTL_INT(_hw_pci, OID_AUTO, realloc_bars, CTLFLAG_RW,
+    &pci_do_realloc_bars, 0,
+    "Attempt to allocate a new range for any BARs whose original firmware-assigned ranges fail to allocate during the initial device scan.");
+
 static int pci_do_power_nodriver = 0;
 TUNABLE_INT("hw.pci.do_power_nodriver", &pci_do_power_nodriver);
 SYSCTL_INT(_hw_pci, OID_AUTO, do_power_nodriver, CTLFLAG_RW,
@@ -2816,13 +2822,34 @@ pci_add_map(device_t bus, device_t dev, 
 	 */
 	res = resource_list_reserve(rl, bus, dev, type, &reg, start, end, count,
 	    prefetch ? RF_PREFETCHABLE : 0);
+	if (pci_do_realloc_bars && res == NULL && (start != 0 || end != ~0ul)) {
+		/*
+		 * If the allocation fails, try to allocate a resource for
+		 * this BAR using any available range.  The firmware felt
+		 * it was important enough to assign a resource, so don't
+		 * disable decoding if we can help it.
+		 */
+		resource_list_delete(rl, type, reg);
+		resource_list_add(rl, type, reg, 0, ~0ul, count);
+		res = resource_list_reserve(rl, bus, dev, type, &reg, 0, ~0ul,
+		    count, prefetch ? RF_PREFETCHABLE : 0);
+	}
 	if (res == NULL) {
 		/*
 		 * If the allocation fails, delete the resource list entry
-		 * to force pci_alloc_resource() to allocate resources
-		 * from the parent.
+		 * and disable decoding for this device.
+		 *
+		 * If the driver requests this resource in the future,
+		 * pci_reserve_map() will try to allocate a fresh
+		 * resource range.
 		 */
 		resource_list_delete(rl, type, reg);
+		pci_disable_io(dev, type);
+		if (bootverbose)
+			device_printf(bus,
+			    "pci%d:%d:%d:%d bar %#x failed to allocate\n",
+			    pci_get_domain(dev), pci_get_bus(dev),
+			    pci_get_slot(dev), pci_get_function(dev), reg);
 	} else {
 		start = rman_get_start(res);
 		pci_write_bar(dev, pm, start);



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