summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2014-04-14 23:25:54 +0200
committerBjorn Helgaas <bhelgaas@google.com>2014-05-23 18:47:19 +0200
commit23b13bc76f359e99140baf083dc44314f4eb1b87 (patch)
tree4ad84a406118d70534b6b199e78646b052e57df9 /drivers/pci
parentx86/gart: Tidy messages and add bridge device info (diff)
downloadlinux-23b13bc76f359e99140baf083dc44314f4eb1b87.tar.xz
linux-23b13bc76f359e99140baf083dc44314f4eb1b87.zip
PCI: Fail safely if we can't handle BARs larger than 4GB
We can only handle BARs larger than 4GB if both dma_addr_t and resource_size_t are 64 bits wide. If dma_addr_t is 32 bits, we can't represent all the bus addresses, and if resource_size_t is 32 bits, we can't represent all the CPU addresses. Previously we cleared res->flags (at "fail:") for resources that were too large. That means we think the BAR doesn't exist at all, which in turn means that we could enable the device even though we can't keep track of where the BAR is and we can't make sure it doesn't overlap something else. This preserves the type flags (MEM/IO) so we can keep from enabling the device. Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/probe.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index ef09f5f2fe6c..c7f8b717c2e7 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -171,6 +171,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
struct resource *res, unsigned int pos)
{
u32 l, sz, mask;
+ u64 l64, sz64, mask64;
u16 orig_cmd;
struct pci_bus_region region, inverted_region;
bool bar_too_big = false, bar_disabled = false;
@@ -226,9 +227,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
}
if (res->flags & IORESOURCE_MEM_64) {
- u64 l64 = l;
- u64 sz64 = sz;
- u64 mask64 = mask | (u64)~0 << 32;
+ l64 = l;
+ sz64 = sz;
+ mask64 = mask | (u64)~0 << 32;
pci_read_config_dword(dev, pos + 4, &l);
pci_write_config_dword(dev, pos + 4, ~0);
@@ -243,9 +244,13 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
if (!sz64)
goto fail;
- if ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) {
+ if ((sizeof(dma_addr_t) < 8 || sizeof(resource_size_t) < 8) &&
+ sz64 > 0x100000000ULL) {
+ res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
+ res->start = 0;
+ res->end = 0;
bar_too_big = true;
- goto fail;
+ goto out;
}
if ((sizeof(resource_size_t) < 8) && l) {
@@ -303,7 +308,8 @@ out:
pci_write_config_word(dev, PCI_COMMAND, orig_cmd);
if (bar_too_big)
- dev_err(&dev->dev, "reg 0x%x: can't handle 64-bit BAR\n", pos);
+ dev_err(&dev->dev, "reg 0x%x: can't handle BAR larger than 4GB (size %#010llx)\n",
+ pos, (unsigned long long) sz64);
if (res->flags && !bar_disabled)
dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res);