Skip to content

Commit

Permalink
Merge branch 'pci/mjg-pci-roms-from-efi' into next
Browse files Browse the repository at this point in the history
* pci/mjg-pci-roms-from-efi:
  x86: Use PCI setup data
  PCI: Add support for non-BAR ROMs
  PCI: Add pcibios_add_device
  EFI: Stash ROMs if they're not in the PCI BAR
  • Loading branch information
Bjorn Helgaas committed Dec 6, 2012
2 parents edb1daa + f9a37be commit 72e1e86
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 6 deletions.
118 changes: 118 additions & 0 deletions arch/x86/boot/compressed/eboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* ----------------------------------------------------------------------- */

#include <linux/efi.h>
#include <linux/pci.h>
#include <asm/efi.h>
#include <asm/setup.h>
#include <asm/desc.h>
Expand Down Expand Up @@ -243,6 +244,121 @@ static void find_bits(unsigned long mask, u8 *pos, u8 *size)
*size = len;
}

static efi_status_t setup_efi_pci(struct boot_params *params)
{
efi_pci_io_protocol *pci;
efi_status_t status;
void **pci_handle;
efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID;
unsigned long nr_pci, size = 0;
int i;
struct setup_data *data;

data = (struct setup_data *)params->hdr.setup_data;

while (data && data->next)
data = (struct setup_data *)data->next;

status = efi_call_phys5(sys_table->boottime->locate_handle,
EFI_LOCATE_BY_PROTOCOL, &pci_proto,
NULL, &size, pci_handle);

if (status == EFI_BUFFER_TOO_SMALL) {
status = efi_call_phys3(sys_table->boottime->allocate_pool,
EFI_LOADER_DATA, size, &pci_handle);

if (status != EFI_SUCCESS)
return status;

status = efi_call_phys5(sys_table->boottime->locate_handle,
EFI_LOCATE_BY_PROTOCOL, &pci_proto,
NULL, &size, pci_handle);
}

if (status != EFI_SUCCESS)
goto free_handle;

nr_pci = size / sizeof(void *);
for (i = 0; i < nr_pci; i++) {
void *h = pci_handle[i];
uint64_t attributes;
struct pci_setup_rom *rom;

status = efi_call_phys3(sys_table->boottime->handle_protocol,
h, &pci_proto, &pci);

if (status != EFI_SUCCESS)
continue;

if (!pci)
continue;

status = efi_call_phys4(pci->attributes, pci,
EfiPciIoAttributeOperationGet, 0,
&attributes);

if (status != EFI_SUCCESS)
continue;

if (!attributes & EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM)
continue;

if (!pci->romimage || !pci->romsize)
continue;

size = pci->romsize + sizeof(*rom);

status = efi_call_phys3(sys_table->boottime->allocate_pool,
EFI_LOADER_DATA, size, &rom);

if (status != EFI_SUCCESS)
continue;

rom->data.type = SETUP_PCI;
rom->data.len = size - sizeof(struct setup_data);
rom->data.next = 0;
rom->pcilen = pci->romsize;

status = efi_call_phys5(pci->pci.read, pci,
EfiPciIoWidthUint16, PCI_VENDOR_ID,
1, &(rom->vendor));

if (status != EFI_SUCCESS)
goto free_struct;

status = efi_call_phys5(pci->pci.read, pci,
EfiPciIoWidthUint16, PCI_DEVICE_ID,
1, &(rom->devid));

if (status != EFI_SUCCESS)
goto free_struct;

status = efi_call_phys5(pci->get_location, pci,
&(rom->segment), &(rom->bus),
&(rom->device), &(rom->function));

if (status != EFI_SUCCESS)
goto free_struct;

memcpy(rom->romdata, pci->romimage, pci->romsize);

if (data)
data->next = (uint64_t)rom;
else
params->hdr.setup_data = (uint64_t)rom;

data = (struct setup_data *)rom;

continue;
free_struct:
efi_call_phys1(sys_table->boottime->free_pool, rom);
}

free_handle:
efi_call_phys1(sys_table->boottime->free_pool, pci_handle);
return status;
}

/*
* See if we have Graphics Output Protocol
*/
Expand Down Expand Up @@ -1026,6 +1142,8 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table,

setup_graphics(boot_params);

setup_efi_pci(boot_params);

status = efi_call_phys3(sys_table->boottime->allocate_pool,
EFI_LOADER_DATA, sizeof(*gdt),
(void **)&gdt);
Expand Down
1 change: 1 addition & 0 deletions arch/x86/include/asm/bootparam.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define SETUP_NONE 0
#define SETUP_E820_EXT 1
#define SETUP_DTB 2
#define SETUP_PCI 3

/* extensible setup data list node */
struct setup_data {
Expand Down
12 changes: 12 additions & 0 deletions arch/x86/include/asm/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,16 @@ cpumask_of_pcibus(const struct pci_bus *bus)
}
#endif

struct pci_setup_rom {
struct setup_data data;
uint16_t vendor;
uint16_t devid;
uint64_t pcilen;
unsigned long segment;
unsigned long bus;
unsigned long device;
unsigned long function;
uint8_t romdata[0];
};

#endif /* _ASM_X86_PCI_H */
4 changes: 0 additions & 4 deletions arch/x86/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,7 @@ int default_check_phys_apicid_present(int phys_apicid)
}
#endif

#ifndef CONFIG_DEBUG_BOOT_PARAMS
struct boot_params __initdata boot_params;
#else
struct boot_params boot_params;
#endif

/*
* Machine setup..
Expand Down
30 changes: 30 additions & 0 deletions arch/x86/pci/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/pci_x86.h>
#include <asm/setup.h>

unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 |
PCI_PROBE_MMCONF;
Expand Down Expand Up @@ -608,6 +609,35 @@ unsigned int pcibios_assign_all_busses(void)
return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
}

int pcibios_add_device(struct pci_dev *dev)
{
struct setup_data *data;
struct pci_setup_rom *rom;
u64 pa_data;

pa_data = boot_params.hdr.setup_data;
while (pa_data) {
data = phys_to_virt(pa_data);

if (data->type == SETUP_PCI) {
rom = (struct pci_setup_rom *)data;

if ((pci_domain_nr(dev->bus) == rom->segment) &&
(dev->bus->number == rom->bus) &&
(PCI_SLOT(dev->devfn) == rom->device) &&
(PCI_FUNC(dev->devfn) == rom->function) &&
(dev->vendor == rom->vendor) &&
(dev->device == rom->devid)) {
dev->rom = (void *)(unsigned long)(pa_data +
offsetof(struct pci_setup_rom, romdata));
dev->romlen = rom->pcilen;
}
}
pa_data = data->next;
}
return 0;
}

int pcibios_enable_device(struct pci_dev *dev, int mask)
{
int err;
Expand Down
5 changes: 5 additions & 0 deletions drivers/pci/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ int pci_bus_add_device(struct pci_dev *dev)
int retval;

pci_fixup_device(pci_fixup_final, dev);

retval = pcibios_add_device(dev);
if (retval)
return retval;

retval = device_add(&dev->dev);
if (retval)
return retval;
Expand Down
13 changes: 13 additions & 0 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,19 @@ void pcim_pin_device(struct pci_dev *pdev)
dr->pinned = 1;
}

/*
* pcibios_add_device - provide arch specific hooks when adding device dev
* @dev: the PCI device being added
*
* Permits the platform to provide architecture specific functionality when
* devices are added. This is the default implementation. Architecture
* implementations can override this.
*/
int __weak pcibios_add_device (struct pci_dev *dev)
{
return 0;
}

/**
* pcibios_disable_device - disable arch specific PCI resources for device dev
* @dev: the PCI device to disable
Expand Down
11 changes: 9 additions & 2 deletions drivers/pci/rom.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,18 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
loff_t start;
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((phys_addr_t)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.
*/
if (res->flags & IORESOURCE_ROM_SHADOW) {
} else if (res->flags & IORESOURCE_ROM_SHADOW) {
/* primary video rom always starts here */
start = (loff_t)0xC0000;
*size = 0x20000; /* cover C000:0 through E000:0 */
Expand Down Expand Up @@ -181,7 +187,8 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY))
return;

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

/* Disable again before continuing, leave enabled if pci=rom */
if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
Expand Down
71 changes: 71 additions & 0 deletions include/linux/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,77 @@ typedef struct {
void *create_event_ex;
} efi_boot_services_t;

typedef enum {
EfiPciIoWidthUint8,
EfiPciIoWidthUint16,
EfiPciIoWidthUint32,
EfiPciIoWidthUint64,
EfiPciIoWidthFifoUint8,
EfiPciIoWidthFifoUint16,
EfiPciIoWidthFifoUint32,
EfiPciIoWidthFifoUint64,
EfiPciIoWidthFillUint8,
EfiPciIoWidthFillUint16,
EfiPciIoWidthFillUint32,
EfiPciIoWidthFillUint64,
EfiPciIoWidthMaximum
} EFI_PCI_IO_PROTOCOL_WIDTH;

typedef enum {
EfiPciIoAttributeOperationGet,
EfiPciIoAttributeOperationSet,
EfiPciIoAttributeOperationEnable,
EfiPciIoAttributeOperationDisable,
EfiPciIoAttributeOperationSupported,
EfiPciIoAttributeOperationMaximum
} EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION;


typedef struct {
void *read;
void *write;
} efi_pci_io_protocol_access_t;

typedef struct {
void *poll_mem;
void *poll_io;
efi_pci_io_protocol_access_t mem;
efi_pci_io_protocol_access_t io;
efi_pci_io_protocol_access_t pci;
void *copy_mem;
void *map;
void *unmap;
void *allocate_buffer;
void *free_buffer;
void *flush;
void *get_location;
void *attributes;
void *get_bar_attributes;
void *set_bar_attributes;
uint64_t romsize;
void *romimage;
} efi_pci_io_protocol;

#define EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO 0x0001
#define EFI_PCI_IO_ATTRIBUTE_ISA_IO 0x0002
#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO 0x0004
#define EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY 0x0008
#define EFI_PCI_IO_ATTRIBUTE_VGA_IO 0x0010
#define EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO 0x0020
#define EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO 0x0040
#define EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080
#define EFI_PCI_IO_ATTRIBUTE_IO 0x0100
#define EFI_PCI_IO_ATTRIBUTE_MEMORY 0x0200
#define EFI_PCI_IO_ATTRIBUTE_BUS_MASTER 0x0400
#define EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED 0x0800
#define EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE 0x1000
#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE 0x2000
#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM 0x4000
#define EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE 0x8000
#define EFI_PCI_IO_ATTRIBUTE_ISA_IO_16 0x10000
#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 0x20000
#define EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 0x40000

/*
* Types and defines for EFI ResetSystem
*/
Expand Down
3 changes: 3 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ struct pci_dev {
};
struct pci_ats *ats; /* Address Translation Service */
#endif
void *rom; /* Physical pointer to ROM if it's not from the BAR */
size_t romlen; /* Length of ROM if it's not from the BAR */
};

static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
Expand Down Expand Up @@ -1599,6 +1601,7 @@ void pcibios_disable_device(struct pci_dev *dev);
void pcibios_set_master(struct pci_dev *dev);
int pcibios_set_pcie_reset_state(struct pci_dev *dev,
enum pcie_reset_state state);
int pcibios_add_device(struct pci_dev *dev);

#ifdef CONFIG_PCI_MMCONFIG
extern void __init pci_mmcfg_early_init(void);
Expand Down

0 comments on commit 72e1e86

Please sign in to comment.