Some bios put range that is not fully coverred by root bus resources.
Try to clip them and update them in pci bridge bars.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=85491
Reported-by: Marek Kordik <kordikma...@gmail.com>
Tested-by: Marek Kordik <kordikma...@gmail.com>
Fixes: 5b28541552ef ("PCI: Restrict 64-bit prefetchable bridge windows to 
64-bit resources")
Signed-off-by: Yinghai Lu <ying...@kernel.org>
Cc: Thomas Gleixner <t...@linutronix.de>
Cc: Ingo Molnar <mi...@redhat.com>
Cc: "H. Peter Anvin" <h...@zytor.com>
Cc: x...@kernel.org
---
 arch/x86/pci/i386.c | 74 +++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 52 insertions(+), 22 deletions(-)

diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c
index 9b18ef3..d43e1af 100644
--- a/arch/x86/pci/i386.c
+++ b/arch/x86/pci/i386.c
@@ -205,10 +205,11 @@ EXPORT_SYMBOL(pcibios_align_resource);
  *         as well.
  */
 
-static void pcibios_allocate_bridge_resources(struct pci_dev *dev)
+static bool pcibios_allocate_bridge_resources(struct pci_dev *dev)
 {
        int idx;
        struct resource *r;
+       bool changed = false;
 
        for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) {
                r = &dev->resource[idx];
@@ -216,17 +217,33 @@ static void pcibios_allocate_bridge_resources(struct 
pci_dev *dev)
                        continue;
                if (r->parent)  /* Already allocated */
                        continue;
-               if (!r->start || pci_claim_resource(dev, idx) < 0) {
-                       /*
-                        * Something is wrong with the region.
-                        * Invalidate the resource to prevent
-                        * child resource allocations in this
-                        * range.
-                        */
-                       r->start = r->end = 0;
-                       r->flags = 0;
+
+               if (!r->start)
+                       goto clear;
+
+               if (pci_claim_resource(dev, idx) >= 0)
+                       continue;
+
+               /* try again after clip for pci bridge*/
+               if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI &&
+                   pci_bus_clip_resource(dev, r)) {
+                       changed = true;
+                       if (pci_claim_resource(dev, idx) >= 0)
+                               continue;
                }
+
+clear:
+               /*
+                * Something is wrong with the region.
+                * Invalidate the resource to prevent
+                * child resource allocations in this
+                * range.
+                */
+               r->start = r->end = 0;
+               r->flags = 0;
        }
+
+       return changed;
 }
 
 static void pcibios_allocate_bus_resources(struct pci_bus *bus)
@@ -234,8 +251,12 @@ static void pcibios_allocate_bus_resources(struct pci_bus 
*bus)
        struct pci_bus *child;
 
        /* Depth-First Search on bus tree */
-       if (bus->self)
-               pcibios_allocate_bridge_resources(bus->self);
+       if (bus->self) {
+               bool changed = pcibios_allocate_bridge_resources(bus->self);
+
+               if (changed)
+                       pci_setup_bridge(bus);
+       }
        list_for_each_entry(child, &bus->children, node)
                pcibios_allocate_bus_resources(child);
 }
@@ -274,18 +295,27 @@ static void pcibios_allocate_dev_resources(struct pci_dev 
*dev, int pass)
                                dev_dbg(&dev->dev,
                                        "BAR %d: reserving %pr (d=%d, p=%d)\n",
                                        idx, r, disabled, pass);
-                               if (pci_claim_resource(dev, idx) < 0) {
-                                       if (r->flags & IORESOURCE_PCI_FIXED) {
-                                               dev_info(&dev->dev, "BAR %d %pR 
is immovable\n",
+
+                               if (pci_claim_resource(dev, idx) >= 0)
+                                       continue;
+
+                               if (r->flags & IORESOURCE_PCI_FIXED) {
+                                       dev_info(&dev->dev, "BAR %d %pR is 
immovable\n",
                                                         idx, r);
-                                       } else {
-                                               /* We'll assign a new address 
later */
-                                               pcibios_save_fw_addr(dev,
-                                                               idx, r->start);
-                                               r->end -= r->start;
-                                               r->start = 0;
-                                       }
+                                       continue;
+                               }
+
+                               /* try again with clip */
+                               if (pci_bus_clip_resource(dev, r)) {
+                                       pci_update_resource(dev, idx);
+                                       if (pci_claim_resource(dev, idx) >= 0)
+                                               continue;
                                }
+
+                               /* We'll assign a new address later */
+                               pcibios_save_fw_addr(dev, idx, r->start);
+                               r->end -= r->start;
+                               r->start = 0;
                        }
                }
        if (!pass) {
-- 
1.8.4.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to