diff options
Diffstat (limited to 'drivers/pci/rom.c')
| -rw-r--r-- | drivers/pci/rom.c | 187 |
1 files changed, 88 insertions, 99 deletions
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 598a115cd00..f955edb9bea 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -6,8 +6,8 @@ * * PCI ROM access routines */ -#include <linux/config.h> #include <linux/kernel.h> +#include <linux/export.h> #include <linux/pci.h> #include <linux/slab.h> @@ -22,7 +22,7 @@ * between the ROM and other resources, so enabling it may disable access * to MMIO registers or other card memory. */ -static int pci_enable_rom(struct pci_dev *pdev) +int pci_enable_rom(struct pci_dev *pdev) { struct resource *res = pdev->resource + PCI_ROM_RESOURCE; struct pci_bus_region region; @@ -31,13 +31,14 @@ static int pci_enable_rom(struct pci_dev *pdev) if (!res->flags) return -1; - pcibios_resource_to_bus(pdev, ®ion, res); + 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 @@ -46,19 +47,67 @@ static int pci_enable_rom(struct pci_dev *pdev) * Disable ROM decoding on a PCI device by turning off the last bit in the * ROM BAR. */ -static void pci_disable_rom(struct pci_dev *pdev) +void pci_disable_rom(struct pci_dev *pdev) { u32 rom_addr; 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 + * @pdev: target PCI device + * @rom: kernel virtual pointer to image of ROM + * @size: size of PCI window + * return: size of actual ROM image + * + * Determine the actual length of the ROM image. + * 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) +{ + void __iomem *image; + int last_image; + + 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"); + break; + } + if (readb(image + 1) != 0xAA) + break; + /* get the PCI data structure and check its 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') + break; + last_image = readb(pds + 21) & 0x80; + /* this length is reliable */ + image += readw(pds + 16) * 512; + } while (!last_image); + + /* never return a size larger than the PCI resource window */ + /* there are known ROMs that get the size wrong */ + return min((size_t)(image - rom), size); +} /** * pci_map_rom - map a PCI ROM to kernel space * @pdev: pointer to pci device struct * @size: pointer to receive size of pci window over ROM - * @return: kernel virtual pointer to image of ROM + * + * Return: kernel virtual pointer to image of ROM * * Map a PCI ROM into kernel space. If ROM is boot video ROM, * the shadow BIOS copy will be returned instead of the @@ -69,23 +118,26 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; loff_t start; void __iomem *rom; - void __iomem *image; - int last_image; - /* IORESOURCE_ROM_SHADOW only set on x86 */ + /* + * 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) { + if (res->flags & + (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY)) { *size = pci_resource_len(pdev, PCI_ROM_RESOURCE); - return (void __iomem *)pci_resource_start(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)) + 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); @@ -113,69 +165,10 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) * size is much larger than the actual size of the ROM. * True size is important if the ROM is going to be copied. */ - image = rom; - do { - void __iomem *pds; - /* Standard PCI ROMs start out with these bytes 55 AA */ - if (readb(image) != 0x55) - break; - if (readb(image + 1) != 0xAA) - break; - /* get the PCI data structure and check its 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') - break; - last_image = readb(pds + 21) & 0x80; - /* this length is reliable */ - image += readw(pds + 16) * 512; - } while (!last_image); - - /* never return a size larger than the PCI resource window */ - /* there are known ROMs that get the size wrong */ - *size = min((size_t)(image - rom), *size); - + *size = pci_get_rom_size(pdev, rom, *size); return rom; } - -/** - * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy - * @pdev: pointer to pci device struct - * @size: pointer to receive size of pci window over ROM - * @return: kernel virtual pointer to image of ROM - * - * Map a PCI ROM into kernel space. If ROM is boot video ROM, - * the shadow BIOS copy will be returned instead of the - * actual ROM. - */ -void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size) -{ - struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; - void __iomem *rom; - - rom = pci_map_rom(pdev, size); - if (!rom) - return NULL; - - if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW)) - return rom; - - res->start = (unsigned long)kmalloc(*size, GFP_KERNEL); - if (!res->start) - return rom; - - res->end = res->start + *size; - memcpy_fromio((void*)res->start, rom, *size); - pci_unmap_rom(pdev, rom); - res->flags |= IORESOURCE_ROM_COPY; - - return (void __iomem *)res->start; -} +EXPORT_SYMBOL(pci_map_rom); /** * pci_unmap_rom - unmap the ROM from kernel space @@ -188,7 +181,7 @@ 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) + if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY)) return; iounmap(rom); @@ -197,28 +190,10 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom) if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW))) pci_disable_rom(pdev); } +EXPORT_SYMBOL(pci_unmap_rom); /** - * pci_remove_rom - disable the ROM and remove its sysfs attribute - * @pdev: pointer to pci device struct - * - * Remove the rom file in sysfs and disable ROM decoding. - */ -void pci_remove_rom(struct pci_dev *pdev) -{ - struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; - - if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) - sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); - if (!(res->flags & (IORESOURCE_ROM_ENABLE | - IORESOURCE_ROM_SHADOW | - IORESOURCE_ROM_COPY))) - pci_disable_rom(pdev); -} - -/** - * pci_cleanup_rom - internal routine for freeing the ROM copy created - * by pci_map_rom_copy called from remove.c + * 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. @@ -226,15 +201,29 @@ void pci_remove_rom(struct pci_dev *pdev) void pci_cleanup_rom(struct pci_dev *pdev) { struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; + if (res->flags & IORESOURCE_ROM_COPY) { - kfree((void*)res->start); + kfree((void *)(unsigned long)res->start); + res->flags |= IORESOURCE_UNSET; res->flags &= ~IORESOURCE_ROM_COPY; res->start = 0; res->end = 0; } } -EXPORT_SYMBOL(pci_map_rom); -EXPORT_SYMBOL(pci_map_rom_copy); -EXPORT_SYMBOL(pci_unmap_rom); -EXPORT_SYMBOL(pci_remove_rom); +/** + * 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_platform_rom); |
