Skip to content

Commit

Permalink
PCI: Add PCI ROM helper for platform-provided ROM images
Browse files Browse the repository at this point in the history
It turns out that some UEFI systems provide apparently an apparently valid
PCI ROM BAR that turns out to contain garbage, so the attempt in 547b524
to prefer the ROM from the BAR actually breaks a different set of machines.
As Linus pointed out, the graphics drivers are probably in the best
position to make this judgement, so this basically reverts 547b524 and
f9a37be and adds a new helper function. Followup patches will add support
to nouveau and radeon for probing this ROM source if they can't find a ROM
from some other source.

[bhelgaas: added reporter and bugzilla pointers, s/f4eb5ff05/547b52463]
Reference: https://bugzilla.redhat.com/show_bug.cgi?id=927451
Reference: http://lkml.kernel.org/r/kg69ef$vdb$1@ger.gmane.org
Reported-by: Mantas Mikulėnas <grawity@gmail.com>
Reported-by: Chris Murphy <bugzilla@colorremedies.com>
Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
  • Loading branch information
Matthew Garrett authored and Bjorn Helgaas committed Mar 26, 2013
1 parent 8bb9660 commit fffe01f
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 36 deletions.
67 changes: 31 additions & 36 deletions drivers/pci/rom.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,27 +100,6 @@ 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
Expand All @@ -135,7 +114,7 @@ static loff_t pci_find_rom(struct pci_dev *pdev, 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 = 0;
loff_t start;
void __iomem *rom;

/*
Expand All @@ -154,21 +133,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 {
start = pci_find_rom(pdev, size);
}
}
/* 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;

/*
* 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);
/* Enable ROM space decodes */
if (pci_enable_rom(pdev))
return NULL;
}
}

if (!start)
return NULL;

rom = ioremap(start, *size);
if (!rom) {
/* restore enable if ioremap fails */
Expand Down Expand Up @@ -202,8 +181,7 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY))
return;

if (!pdev->rom || !pdev->romlen)
iounmap(rom);
iounmap(rom);

/* Disable again before continuing, leave enabled if pci=rom */
if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
Expand All @@ -227,7 +205,24 @@ void pci_cleanup_rom(struct pci_dev *pdev)
}
}

/**
* 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);
1 change: 1 addition & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,7 @@ void pci_disable_rom(struct pci_dev *pdev);
void __iomem __must_check *pci_map_rom(struct pci_dev *pdev, size_t *size);
void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom);
size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size);
void __iomem __must_check *pci_platform_rom(struct pci_dev *pdev, size_t *size);

/* Power management related routines */
int pci_save_state(struct pci_dev *dev);
Expand Down

0 comments on commit fffe01f

Please sign in to comment.