diff options
author | jhb <jhb@FreeBSD.org> | 2013-05-09 19:24:50 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2013-05-09 19:24:50 +0000 |
commit | 8f00f63dab2397c14cafd7b0afbdf84350fe5e2b (patch) | |
tree | c97d229122e0d9d60c1094407aa932df02d2ba65 | |
parent | e51caee7844036c335fdcef0aef1e80750642cbb (diff) | |
download | FreeBSD-src-8f00f63dab2397c14cafd7b0afbdf84350fe5e2b.zip FreeBSD-src-8f00f63dab2397c14cafd7b0afbdf84350fe5e2b.tar.gz |
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
-rw-r--r-- | sys/dev/pci/pci.c | 31 |
1 files changed, 29 insertions, 2 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 79d5e57..2851ab7 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -280,6 +280,12 @@ SYSCTL_INT(_hw_pci, OID_AUTO, enable_io_modes, CTLFLAG_RW, 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, int reg, struct resource_list *rl, */ res = resource_list_reserve(rl, bus, dev, type, ®, 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, ®, 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); |