summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2012-12-05 14:33:26 -0700
committerBjorn Helgaas <bhelgaas@google.com>2012-12-05 14:33:26 -0700
commitdd5fc854de5fd37adfcef8a366cd21a55aa01d3d (patch)
tree48658057b6a005c93dfb54072585f432a96e934a
parent8f0d8163b50e01f398b14bcd4dc039ac5ab18d64 (diff)
downloadop-kernel-dev-dd5fc854de5fd37adfcef8a366cd21a55aa01d3d.zip
op-kernel-dev-dd5fc854de5fd37adfcef8a366cd21a55aa01d3d.tar.gz
EFI: Stash ROMs if they're not in the PCI BAR
EFI provides support for providing PCI ROMs via means other than the ROM BAR. This support vanishes after we've exited boot services, so add support for stashing copies of the ROMs in setup_data if they're not otherwise available. Signed-off-by: Matthew Garrett <mjg@redhat.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Tested-by: Seth Forshee <seth.forshee@canonical.com>
-rw-r--r--arch/x86/boot/compressed/eboot.c118
-rw-r--r--arch/x86/include/asm/bootparam.h1
-rw-r--r--arch/x86/include/asm/pci.h12
-rw-r--r--include/linux/efi.h71
4 files changed, 202 insertions, 0 deletions
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index c760e07..8a54313 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 2ad874c..92862cd 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 6e41b93..dba7805 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/include/linux/efi.h b/include/linux/efi.h
index 8670eb1..8eb1be1 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
*/
OpenPOWER on IntegriCloud