forked from Minki/linux
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>
This commit is contained in:
parent
8291550f84
commit
8f38eaca55
@ -281,10 +281,11 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child)
|
||||
|
||||
if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
|
||||
u16 io_base_hi, io_limit_hi;
|
||||
|
||||
pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi);
|
||||
pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi);
|
||||
base |= (io_base_hi << 16);
|
||||
limit |= (io_limit_hi << 16);
|
||||
base |= ((unsigned long) io_base_hi << 16);
|
||||
limit |= ((unsigned long) io_limit_hi << 16);
|
||||
}
|
||||
|
||||
if (base && base <= limit) {
|
||||
@ -312,8 +313,8 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child)
|
||||
res = child->resource[1];
|
||||
pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo);
|
||||
pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
|
||||
base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
|
||||
limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
|
||||
base = ((unsigned long) mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
|
||||
limit = ((unsigned long) mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
|
||||
if (base && base <= limit) {
|
||||
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
|
||||
region.start = base;
|
||||
@ -334,11 +335,12 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child)
|
||||
res = child->resource[2];
|
||||
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
|
||||
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
|
||||
base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
|
||||
limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
|
||||
base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
|
||||
limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
|
||||
|
||||
if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
|
||||
u32 mem_base_hi, mem_limit_hi;
|
||||
|
||||
pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi);
|
||||
pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi);
|
||||
|
||||
@ -349,8 +351,8 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child)
|
||||
*/
|
||||
if (mem_base_hi <= mem_limit_hi) {
|
||||
#if BITS_PER_LONG == 64
|
||||
base |= ((long) mem_base_hi) << 32;
|
||||
limit |= ((long) mem_limit_hi) << 32;
|
||||
base |= ((unsigned long) mem_base_hi) << 32;
|
||||
limit |= ((unsigned long) mem_limit_hi) << 32;
|
||||
#else
|
||||
if (mem_base_hi || mem_limit_hi) {
|
||||
dev_err(&dev->dev, "can't handle 64-bit "
|
||||
|
Loading…
Reference in New Issue
Block a user