Patchwork [3/3] PCI: unset the resource if a wrong bus address is assigned by firmware

login
register
mail settings
Submitter Kevin Hao
Date May 25, 2013, 11:36 a.m.
Message ID <1369481787-21500-4-git-send-email-haokexin@gmail.com>
Download mbox | patch
Permalink /patch/246327/
State Accepted
Headers show

Comments

Kevin Hao - May 25, 2013, 11:36 a.m.
In some situations, the bus and memory address used by kernel are
not equal and the firmware may assign a bus address which happen
to be a legitimate memory address to a PCI device, then the kernel
would not find a matching bus region in host bridge and assume an
offset of zero and translate it to a memory address the same as the
bus address. This will leave the bus address in the PCI BAR register
unchanged and this address is definitely not a legal bus address,
then cause the device malfunction. We try to detect this by doing
a invert translation. We can make sure that we run into this case
if the value we get from the invert translation is not equal to the
original bus address. In this case we would unset this resource and
wish the kernel would trigger a reassign later.

Signed-off-by: Kevin Hao <haokexin@gmail.com>
---
v2:
Instead of tweak the pcibios_bus_to_resource and assume we know
about all the host bridge window, we do an invert translation to
check if we have a wrong bus address set in the PCI BAR register.

 drivers/pci/probe.c | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

Patch

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index c57eb27..33c2e72 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -170,7 +170,7 @@  int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
 {
 	u32 l, sz, mask;
 	u16 orig_cmd;
-	struct pci_bus_region region;
+	struct pci_bus_region region, inverted_region;
 	bool bar_too_big = false, bar_disabled = false;
 
 	mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
@@ -267,6 +267,29 @@  int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
 
 	pcibios_bus_to_resource(dev, res, &region);
 
+	/*
+	 * In some situations, the bus and memory address used by kernel are
+	 * not equal and the firmware may assign a bus address which happen
+	 * to be a legitimate memory address to a PCI device, then the kernel
+	 * would not find a matching bus region in host bridge and assume an
+	 * offset of zero and translate it to a memory address the same as the
+	 * bus address. This will leave the bus address in the PCI BAR register
+	 * unchanged and this address is definitely not a legal bus address,
+	 * then cause the device malfunction. We try to detect this by doing
+	 * a invert translation. We can make sure that we run into this case
+	 * if the value we get from the invert translation is not equal to the
+	 * original bus address.
+	 */
+	pcibios_resource_to_bus(dev, &inverted_region, res);
+	if (inverted_region.start != region.start ||
+		 inverted_region.end != region.end) {
+		dev_info(&dev->dev, "reg 0x%x: bus address [%pa - %pa] allocated by firmware is not in any of the address regions of PCI host bridge, we would force to reassign it later\n",
+			pos, &region.start, &region.end);
+		res->flags |= IORESOURCE_UNSET;
+		res->end -= res->start;
+		res->start = 0;
+	}
+
 	goto out;