PCI: Use ROM images from firmware only if no other ROM source available
authorMatthew Garrett <matthew.garrett@nebula.com>
Tue, 19 Mar 2013 21:26:57 +0000 (17:26 -0400)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 19 Mar 2013 21:51:14 +0000 (14:51 -0700)
Mantas Mikulėnas reported that his graphics hardware failed to
initialise after commit f9a37be0f02a ("x86: Use PCI setup data").

The aim of this commit was to ensure that ROM images were available on
some Apple systems that don't expose the GPU ROM via any other source.
In this case, UEFI appears to have provided a broken ROM image that we
were using even though there was a perfectly valid ROM available via
other sources.  The simplest way to handle this seems to be to just
re-order pci_map_rom() and leave any firmare-supplied ROM to last.

Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
Tested-by: Mantas Mikulėnas <grawity@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/pci/rom.c

index ab886b7..b41ac77 100644 (file)
@@ -100,6 +100,27 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size)
        return min((size_t)(image - rom), size);
 }
 
+static loff_t pci_find_rom(struct pci_dev *pdev, size_t *size)
+{
+       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+       loff_t start;
+
+       /* assign the ROM an address if it doesn't have one */
+       if (res->parent == NULL && pci_assign_resource(pdev, PCI_ROM_RESOURCE))
+               return 0;
+       start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+       *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+
+       if (*size == 0)
+               return 0;
+
+       /* Enable ROM space decodes */
+       if (pci_enable_rom(pdev))
+               return 0;
+
+       return start;
+}
+
 /**
  * pci_map_rom - map a PCI ROM to kernel space
  * @pdev: pointer to pci device struct
@@ -114,21 +135,15 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size)
 void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
 {
        struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
-       loff_t start;
+       loff_t start = 0;
        void __iomem *rom;
 
        /*
-        * Some devices may provide ROMs via a source other than the BAR
-        */
-       if (pdev->rom && pdev->romlen) {
-               *size = pdev->romlen;
-               return phys_to_virt(pdev->rom);
-       /*
         * IORESOURCE_ROM_SHADOW set on x86, x86_64 and IA64 supports legacy
         * memory map if the VGA enable bit of the Bridge Control register is
         * set for embedded VGA.
         */
-       } else if (res->flags & IORESOURCE_ROM_SHADOW) {
+       if (res->flags & IORESOURCE_ROM_SHADOW) {
                /* primary video rom always starts here */
                start = (loff_t)0xC0000;
                *size = 0x20000; /* cover C000:0 through E000:0 */
@@ -139,21 +154,21 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
                        return (void __iomem *)(unsigned long)
                                pci_resource_start(pdev, PCI_ROM_RESOURCE);
                } else {
-                       /* assign the ROM an address if it doesn't have one */
-                       if (res->parent == NULL &&
-                           pci_assign_resource(pdev,PCI_ROM_RESOURCE))
-                               return NULL;
-                       start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
-                       *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
-                       if (*size == 0)
-                               return NULL;
-
-                       /* Enable ROM space decodes */
-                       if (pci_enable_rom(pdev))
-                               return NULL;
+                       start = pci_find_rom(pdev, size);
                }
        }
 
+       /*
+        * Some devices may provide ROMs via a source other than the BAR
+        */
+       if (!start && pdev->rom && pdev->romlen) {
+               *size = pdev->romlen;
+               return phys_to_virt(pdev->rom);
+       }
+
+       if (!start)
+               return NULL;
+
        rom = ioremap(start, *size);
        if (!rom) {
                /* restore enable if ioremap fails */