提交 58c84eda 编写于 作者: B Bjorn Helgaas 提交者: Jesse Barnes

PCI: fall back to original BIOS BAR addresses

If we fail to assign resources to a PCI BAR, this patch makes us try the
original address from BIOS rather than leaving it disabled.

Linux tries to make sure all PCI device BARs are inside the upstream
PCI host bridge or P2P bridge apertures, reassigning BARs if necessary.
Windows does similar reassignment.

Before this patch, if we could not move a BAR into an aperture, we left
the resource unassigned, i.e., at address zero.  Windows leaves such BARs
at the original BIOS addresses, and this patch makes Linux do the same.

This is a bit ugly because we disable the resource long before we try to
reassign it, so we have to keep track of the BIOS BAR address somewhere.
For lack of a better place, I put it in the struct pci_dev.

I think it would be cleaner to attempt the assignment immediately when the
claim fails, so we could easily remember the original address.  But we
currently claim motherboard resources in the middle, after attempting to
claim PCI resources and before assigning new PCI resources, and changing
that is a fairly big job.

Addresses https://bugzilla.kernel.org/show_bug.cgi?id=16263Reported-by: NAndrew <nitr0@seti.kr.ua>
Tested-by: NAndrew <nitr0@seti.kr.ua>
Signed-off-by: NBjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: NJesse Barnes <jbarnes@virtuousgeek.org>
上级 2f7989ef
...@@ -184,6 +184,7 @@ static void __init pcibios_allocate_resources(int pass) ...@@ -184,6 +184,7 @@ static void __init pcibios_allocate_resources(int pass)
idx, r, disabled, pass); idx, r, disabled, pass);
if (pci_claim_resource(dev, idx) < 0) { if (pci_claim_resource(dev, idx) < 0) {
/* We'll assign a new address later */ /* We'll assign a new address later */
dev->fw_addr[idx] = r->start;
r->end -= r->start; r->end -= r->start;
r->start = 0; r->start = 0;
} }
......
...@@ -156,6 +156,38 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, ...@@ -156,6 +156,38 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
pcibios_align_resource, dev); pcibios_align_resource, dev);
} }
if (ret < 0 && dev->fw_addr[resno]) {
struct resource *root, *conflict;
resource_size_t start, end;
/*
* If we failed to assign anything, let's try the address
* where firmware left it. That at least has a chance of
* working, which is better than just leaving it disabled.
*/
if (res->flags & IORESOURCE_IO)
root = &ioport_resource;
else
root = &iomem_resource;
start = res->start;
end = res->end;
res->start = dev->fw_addr[resno];
res->end = res->start + size - 1;
dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
resno, res);
conflict = request_resource_conflict(root, res);
if (conflict) {
dev_info(&dev->dev,
"BAR %d: %pR conflicts with %s %pR\n", resno,
res, conflict->name, conflict);
res->start = start;
res->end = end;
} else
ret = 0;
}
if (!ret) { if (!ret) {
res->flags &= ~IORESOURCE_STARTALIGN; res->flags &= ~IORESOURCE_STARTALIGN;
dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res); dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
......
...@@ -288,6 +288,7 @@ struct pci_dev { ...@@ -288,6 +288,7 @@ struct pci_dev {
*/ */
unsigned int irq; unsigned int irq;
struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */ struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
resource_size_t fw_addr[DEVICE_COUNT_RESOURCE]; /* FW-assigned addr */
/* These fields are used by common fixups */ /* These fields are used by common fixups */
unsigned int transparent:1; /* Transparent PCI bridge */ unsigned int transparent:1; /* Transparent PCI bridge */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册