diff options
Diffstat (limited to 'drivers/pci/rom.c')
| -rw-r--r-- | drivers/pci/rom.c | 173 |
1 files changed, 71 insertions, 102 deletions
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index c5d0a08a8747..e18d3a4383ba 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -1,10 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/rom.c + * PCI ROM access routines * * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com> * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com> - * - * PCI ROM access routines */ #include <linux/kernel.h> #include <linux/export.h> @@ -24,20 +23,30 @@ */ int pci_enable_rom(struct pci_dev *pdev) { - struct resource *res = pdev->resource + PCI_ROM_RESOURCE; + struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; struct pci_bus_region region; u32 rom_addr; if (!res->flags) return -1; - pcibios_resource_to_bus(pdev, ®ion, res); + /* Nothing to enable if we're using a shadow copy in RAM */ + if (res->flags & IORESOURCE_ROM_SHADOW) + return 0; + + /* + * Ideally pci_update_resource() would update the ROM BAR address, + * and we would only set the enable bit here. But apparently some + * devices have buggy ROM BARs that read as zero when disabled. + */ + pcibios_resource_to_bus(pdev->bus, ®ion, res); pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr); rom_addr &= ~PCI_ROM_ADDRESS_MASK; rom_addr |= region.start | PCI_ROM_ADDRESS_ENABLE; pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr); return 0; } +EXPORT_SYMBOL_GPL(pci_enable_rom); /** * pci_disable_rom - disable ROM decoding for a PCI device @@ -48,11 +57,17 @@ int pci_enable_rom(struct pci_dev *pdev) */ void pci_disable_rom(struct pci_dev *pdev) { + struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; u32 rom_addr; + + if (res->flags & IORESOURCE_ROM_SHADOW) + return; + pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr); rom_addr &= ~PCI_ROM_ADDRESS_ENABLE; pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr); } +EXPORT_SYMBOL_GPL(pci_disable_rom); /** * pci_get_rom_size - obtain the actual size of the ROM image @@ -65,35 +80,42 @@ void pci_disable_rom(struct pci_dev *pdev) * The PCI window size could be much larger than the * actual image size. */ -size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size) +static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, + size_t size) { void __iomem *image; int last_image; + unsigned int length; image = rom; do { void __iomem *pds; /* Standard PCI ROMs start out with these bytes 55 AA */ - if (readb(image) != 0x55) { - dev_err(&pdev->dev, "Invalid ROM contents\n"); + if (readw(image) != 0xAA55) { + pci_info(pdev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n", + readw(image)); break; } - if (readb(image + 1) != 0xAA) - break; - /* get the PCI data structure and check its signature */ + /* get the PCI data structure and check its "PCIR" signature */ pds = image + readw(image + 24); - if (readb(pds) != 'P') - break; - if (readb(pds + 1) != 'C') - break; - if (readb(pds + 2) != 'I') - break; - if (readb(pds + 3) != 'R') + if (readl(pds) != 0x52494350) { + pci_info(pdev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n", + readl(pds)); break; + } last_image = readb(pds + 21) & 0x80; - /* this length is reliable */ - image += readw(pds + 16) * 512; - } while (!last_image); + length = readw(pds + 16); + image += length * 512; + /* Avoid iterating through memory outside the resource window */ + if (image >= rom + size) + break; + if (!last_image) { + if (readw(image) != 0xAA55) { + pci_info(pdev, "No more image in the PCI ROM\n"); + break; + } + } + } while (length && !last_image); /* never return a size larger than the PCI resource window */ /* there are known ROMs that get the size wrong */ @@ -117,46 +139,22 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) loff_t start; void __iomem *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. - */ - if (res->flags & IORESOURCE_ROM_SHADOW) { - /* primary video rom always starts here */ - start = (loff_t)0xC0000; - *size = 0x20000; /* cover C000:0 through E000:0 */ - } else { - if (res->flags & - (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY)) { - *size = pci_resource_len(pdev, PCI_ROM_RESOURCE); - 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; - } - } + /* assign the ROM an address if it doesn't have one */ + if (res->parent == NULL && pci_assign_resource(pdev, PCI_ROM_RESOURCE)) + return NULL; - rom = ioremap(start, *size); - if (!rom) { - /* restore enable if ioremap fails */ - if (!(res->flags & (IORESOURCE_ROM_ENABLE | - IORESOURCE_ROM_SHADOW | - IORESOURCE_ROM_COPY))) - pci_disable_rom(pdev); + 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; + + rom = ioremap(start, *size); + if (!rom) + goto err_ioremap; /* * Try to find the true size of the ROM since sometimes the PCI window @@ -164,8 +162,20 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) * True size is important if the ROM is going to be copied. */ *size = pci_get_rom_size(pdev, rom, *size); + if (!*size) + goto invalid_rom; + return rom; + +invalid_rom: + iounmap(rom); +err_ioremap: + /* restore enable if ioremap fails */ + if (!(res->flags & IORESOURCE_ROM_ENABLE)) + pci_disable_rom(pdev); + return NULL; } +EXPORT_SYMBOL(pci_map_rom); /** * pci_unmap_rom - unmap the ROM from kernel space @@ -178,51 +188,10 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom) { struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; - if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY)) - return; - iounmap(rom); - /* Disable again before continuing, leave enabled if pci=rom */ - if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW))) + /* Disable again before continuing */ + if (!(res->flags & IORESOURCE_ROM_ENABLE)) pci_disable_rom(pdev); } - -/** - * pci_cleanup_rom - free the ROM copy created by pci_map_rom_copy - * @pdev: pointer to pci device struct - * - * Free the copied ROM if we allocated one. - */ -void pci_cleanup_rom(struct pci_dev *pdev) -{ - struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; - if (res->flags & IORESOURCE_ROM_COPY) { - kfree((void*)(unsigned long)res->start); - res->flags &= ~IORESOURCE_ROM_COPY; - res->start = 0; - res->end = 0; - } -} - -/** - * pci_platform_rom - provides a pointer to any ROM image provided by the - * platform - * @pdev: pointer to pci device struct - * @size: pointer to receive size of pci window over ROM - */ -void __iomem *pci_platform_rom(struct pci_dev *pdev, size_t *size) -{ - if (pdev->rom && pdev->romlen) { - *size = pdev->romlen; - return phys_to_virt((phys_addr_t)pdev->rom); - } - - return NULL; -} - -EXPORT_SYMBOL(pci_map_rom); EXPORT_SYMBOL(pci_unmap_rom); -EXPORT_SYMBOL_GPL(pci_enable_rom); -EXPORT_SYMBOL_GPL(pci_disable_rom); -EXPORT_SYMBOL(pci_platform_rom); |
