diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-19 09:45:44 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-20 19:28:53 -0400 |
commit | 8f38eaca55d0fab7499b33adb1dec33e16de5abb (patch) | |
tree | d5f7a0b123d073b8fb75ca46e82e1862f88a9e13 /drivers/pci | |
parent | 8291550f8479fde2cee571d1b367e6918819f189 (diff) |
PCI: fix P2P bridge I/O port window sign extension
On P2P bridges with 32-bit I/O decoding, we incorrectly sign-extended
windows starting at 0x80000000 or above. In "base |= (io_base_hi << 16)",
"io_base_hi" is promoted to a signed int before being extended to an
unsigned long.
This would cause a window starting at I/O address 0x80000000 to be
treated as though it started at 0xffffffff80008000 instead, which
should cause "no compatible bridge window" errors when we enumerate
devices using that I/O space.
The mmio and mmio_pref casts are not strictly necessary, but without
them, correctness depends on the types of the PCI_MEMORY_RANGE_MASK and
PCI_PREF_RANGE_MASK constants, which are not obvious from reading the
local code.
Found by Coverity (CID 138747 and CID 138748).
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/probe.c | 18 |
1 files changed, 10 insertions, 8 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 658ac977cb56..a7a504fc82b9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
@@ -281,10 +281,11 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) | |||
281 | 281 | ||
282 | if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { | 282 | if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { |
283 | u16 io_base_hi, io_limit_hi; | 283 | u16 io_base_hi, io_limit_hi; |
284 | |||
284 | pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi); | 285 | pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi); |
285 | pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi); | 286 | pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi); |
286 | base |= (io_base_hi << 16); | 287 | base |= ((unsigned long) io_base_hi << 16); |
287 | limit |= (io_limit_hi << 16); | 288 | limit |= ((unsigned long) io_limit_hi << 16); |
288 | } | 289 | } |
289 | 290 | ||
290 | if (base && base <= limit) { | 291 | if (base && base <= limit) { |
@@ -312,8 +313,8 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child) | |||
312 | res = child->resource[1]; | 313 | res = child->resource[1]; |
313 | pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); | 314 | pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); |
314 | pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); | 315 | pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); |
315 | base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; | 316 | base = ((unsigned long) mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; |
316 | limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; | 317 | limit = ((unsigned long) mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; |
317 | if (base && base <= limit) { | 318 | if (base && base <= limit) { |
318 | res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; | 319 | res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; |
319 | region.start = base; | 320 | region.start = base; |
@@ -334,11 +335,12 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) | |||
334 | res = child->resource[2]; | 335 | res = child->resource[2]; |
335 | pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); | 336 | pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); |
336 | pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); | 337 | pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); |
337 | base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; | 338 | base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16; |
338 | limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; | 339 | limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; |
339 | 340 | ||
340 | if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { | 341 | if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { |
341 | u32 mem_base_hi, mem_limit_hi; | 342 | u32 mem_base_hi, mem_limit_hi; |
343 | |||
342 | pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi); | 344 | pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi); |
343 | pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi); | 345 | pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi); |
344 | 346 | ||
@@ -349,8 +351,8 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) | |||
349 | */ | 351 | */ |
350 | if (mem_base_hi <= mem_limit_hi) { | 352 | if (mem_base_hi <= mem_limit_hi) { |
351 | #if BITS_PER_LONG == 64 | 353 | #if BITS_PER_LONG == 64 |
352 | base |= ((long) mem_base_hi) << 32; | 354 | base |= ((unsigned long) mem_base_hi) << 32; |
353 | limit |= ((long) mem_limit_hi) << 32; | 355 | limit |= ((unsigned long) mem_limit_hi) << 32; |
354 | #else | 356 | #else |
355 | if (mem_base_hi || mem_limit_hi) { | 357 | if (mem_base_hi || mem_limit_hi) { |
356 | dev_err(&dev->dev, "can't handle 64-bit " | 358 | dev_err(&dev->dev, "can't handle 64-bit " |