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=16263 Reported-by: Andrew <nitr0@seti.kr.ua> Tested-by: Andrew <nitr0@seti.kr.ua> Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
parent
2f7989efd4
commit
58c84eda07
@ -184,6 +184,7 @@ static void __init pcibios_allocate_resources(int pass)
|
||||
idx, r, disabled, pass);
|
||||
if (pci_claim_resource(dev, idx) < 0) {
|
||||
/* We'll assign a new address later */
|
||||
dev->fw_addr[idx] = r->start;
|
||||
r->end -= r->start;
|
||||
r->start = 0;
|
||||
}
|
||||
|
@ -156,6 +156,38 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *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) {
|
||||
res->flags &= ~IORESOURCE_STARTALIGN;
|
||||
dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
|
||||
|
@ -288,6 +288,7 @@ struct pci_dev {
|
||||
*/
|
||||
unsigned int irq;
|
||||
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 */
|
||||
unsigned int transparent:1; /* Transparent PCI bridge */
|
||||
|
Loading…
Reference in New Issue
Block a user