diff options
| -rw-r--r-- | arch/x86/boot/compressed/eboot.c | 118 | ||||
| -rw-r--r-- | arch/x86/include/asm/bootparam.h | 1 | ||||
| -rw-r--r-- | arch/x86/include/asm/pci.h | 12 | ||||
| -rw-r--r-- | arch/x86/kernel/setup.c | 4 | ||||
| -rw-r--r-- | arch/x86/pci/common.c | 30 | ||||
| -rw-r--r-- | drivers/pci/bus.c | 5 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 13 | ||||
| -rw-r--r-- | drivers/pci/rom.c | 11 | ||||
| -rw-r--r-- | include/linux/efi.h | 71 | ||||
| -rw-r--r-- | include/linux/pci.h | 3 | 
10 files changed, 262 insertions, 6 deletions
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index c760e073963e..8a54313bc7dc 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -8,6 +8,7 @@   * ----------------------------------------------------------------------- */  #include <linux/efi.h> +#include <linux/pci.h>  #include <asm/efi.h>  #include <asm/setup.h>  #include <asm/desc.h> @@ -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   */ @@ -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); diff --git a/arch/x86/include/asm/bootparam.h b/arch/x86/include/asm/bootparam.h index 2ad874cb661c..92862cd90201 100644 --- a/arch/x86/include/asm/bootparam.h +++ b/arch/x86/include/asm/bootparam.h @@ -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 { diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 6e41b9343928..dba7805176bf 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -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 */ diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index ca45696f30fb..c228322ca180 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -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.. diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 52dbf1aeeb63..6a6b01778dab 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -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; @@ -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; diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index a543746fb354..ad6a8b635692 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -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; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5b862b1c93c6..7c2e25ee2c5a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -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 diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 0b3037ab8b93..3a3828fbc879 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -118,11 +118,17 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)  	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 */ @@ -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))) diff --git a/include/linux/efi.h b/include/linux/efi.h index 8670eb1eb8cd..8eb1be17c801 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -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   */ diff --git a/include/linux/pci.h b/include/linux/pci.h index a98a5f9ac1d5..ea41a0a52a59 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -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) @@ -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);  | 
