From d7ad2254fa7cc11aec3faeba076c1243f6adeb47 Mon Sep 17 00:00:00 2001 From: John Keller Date: Mon, 9 Jul 2007 11:42:24 -0700 Subject: [PATCH] [IA64] SN: Correct ROM resource length for BIOS copy On SN systems, when setting the IORESOURCE_ROM_BIOS_COPY resource flag, the resource length should be set to the actual size of the ROM image so that a call to pci_map_rom() returns the correct size. Signed-off-by: John Keller Signed-off-by: Andrew Morton Signed-off-by: Tony Luck --- arch/ia64/sn/kernel/io_acpi_init.c | 17 ++++--- arch/ia64/sn/kernel/io_init.c | 20 ++++++-- drivers/pci/rom.c | 73 ++++++++++++++++++------------ include/linux/pci.h | 1 + 4 files changed, 70 insertions(+), 41 deletions(-) diff --git a/arch/ia64/sn/kernel/io_acpi_init.c b/arch/ia64/sn/kernel/io_acpi_init.c index c6216f454ffb..3c7178f5dce8 100644 --- a/arch/ia64/sn/kernel/io_acpi_init.c +++ b/arch/ia64/sn/kernel/io_acpi_init.c @@ -418,7 +418,7 @@ sn_acpi_slot_fixup(struct pci_dev *dev) void __iomem *addr; struct pcidev_info *pcidev_info = NULL; struct sn_irq_info *sn_irq_info = NULL; - size_t size; + size_t image_size, size; if (sn_acpi_get_pcidev_info(dev, &pcidev_info, &sn_irq_info)) { panic("%s: Failure obtaining pcidev_info for %s\n", @@ -428,17 +428,16 @@ sn_acpi_slot_fixup(struct pci_dev *dev) if (pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE]) { /* * A valid ROM image exists and has been shadowed by the - * PROM. Setup the pci_dev ROM resource to point to - * the shadowed copy. + * PROM. Setup the pci_dev ROM resource with the address + * of the shadowed copy, and the actual length of the ROM image. */ - size = dev->resource[PCI_ROM_RESOURCE].end - - dev->resource[PCI_ROM_RESOURCE].start; - addr = - ioremap(pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE], - size); + size = pci_resource_len(dev, PCI_ROM_RESOURCE); + addr = ioremap(pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE], + size); + image_size = pci_get_rom_size(addr, size); dev->resource[PCI_ROM_RESOURCE].start = (unsigned long) addr; dev->resource[PCI_ROM_RESOURCE].end = - (unsigned long) addr + size; + (unsigned long) addr + image_size - 1; dev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_BIOS_COPY; } sn_pci_fixup_slot(dev, pcidev_info, sn_irq_info); diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c index 6b10e5d28488..906b93674b76 100644 --- a/arch/ia64/sn/kernel/io_init.c +++ b/arch/ia64/sn/kernel/io_init.c @@ -259,9 +259,23 @@ sn_io_slot_fixup(struct pci_dev *dev) insert_resource(&ioport_resource, &dev->resource[idx]); else insert_resource(&iomem_resource, &dev->resource[idx]); - /* If ROM, mark as shadowed in PROM */ - if (idx == PCI_ROM_RESOURCE) - dev->resource[idx].flags |= IORESOURCE_ROM_BIOS_COPY; + /* + * If ROM, set the actual ROM image size, and mark as + * shadowed in PROM. + */ + if (idx == PCI_ROM_RESOURCE) { + size_t image_size; + void __iomem *rom; + + rom = ioremap(pci_resource_start(dev, PCI_ROM_RESOURCE), + size + 1); + image_size = pci_get_rom_size(rom, size + 1); + dev->resource[PCI_ROM_RESOURCE].end = + dev->resource[PCI_ROM_RESOURCE].start + + image_size - 1; + dev->resource[PCI_ROM_RESOURCE].flags |= + IORESOURCE_ROM_BIOS_COPY; + } } /* Create a pci_window in the pci_controller struct for * each device resource. diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index d087e0817715..dbbcc04abd1a 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -53,6 +53,49 @@ static void pci_disable_rom(struct pci_dev *pdev) pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr); } +/** + * pci_get_rom_size - obtain the actual size of the ROM image + * @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(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) + 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 @@ -68,8 +111,6 @@ 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 set on x86, x86_64 and IA64 supports legacy @@ -117,33 +158,7 @@ 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(rom, *size); return rom; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 086a0e5a6318..acb9387c0364 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -560,6 +560,7 @@ void __iomem __must_check *pci_map_rom(struct pci_dev *pdev, size_t *size); void __iomem __must_check *pci_map_rom_copy(struct pci_dev *pdev, size_t *size); void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom); void pci_remove_rom(struct pci_dev *pdev); +size_t pci_get_rom_size(void __iomem *rom, size_t size); /* Power management related routines */ int pci_save_state(struct pci_dev *dev);