summaryrefslogtreecommitdiffstats
path: root/handle-efi-roms.patch
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2012-09-04 11:31:54 -0400
committerMatthew Garrett <mjg@redhat.com>2012-09-04 11:35:16 -0400
commit447e3a6aa1760b6d235df50e7ba8570902ede270 (patch)
treeee7b776d8dc88de068461c148ef8ee16b33c5499 /handle-efi-roms.patch
parent1b72b444f0ff50136c819be540b67ecddbf815df (diff)
downloadkernel-447e3a6aa1760b6d235df50e7ba8570902ede270.tar.gz
kernel-447e3a6aa1760b6d235df50e7ba8570902ede270.tar.xz
kernel-447e3a6aa1760b6d235df50e7ba8570902ede270.zip
handle-efi-roms.patch: Improve PCI support on UEFI systems
Diffstat (limited to 'handle-efi-roms.patch')
-rw-r--r--handle-efi-roms.patch458
1 files changed, 458 insertions, 0 deletions
diff --git a/handle-efi-roms.patch b/handle-efi-roms.patch
new file mode 100644
index 000000000..7f02a1c3d
--- /dev/null
+++ b/handle-efi-roms.patch
@@ -0,0 +1,458 @@
+diff -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/boot/compressed/eboot.c ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/boot/compressed/eboot.c
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/boot/compressed/eboot.c 2012-08-22 15:26:32.485522068 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/boot/compressed/eboot.c 2012-08-22 15:25:40.529244868 -0400
+@@ -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 @@
+ *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 = NULL;
++ 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
+ */
+@@ -276,8 +392,9 @@
+ nr_gops = size / sizeof(void *);
+ for (i = 0; i < nr_gops; i++) {
+ struct efi_graphics_output_mode_info *info;
+- efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
+- void *pciio;
++ efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
++ bool conout_found = false;
++ void *dummy;
+ void *h = gop_handle[i];
+
+ status = efi_call_phys3(sys_table->boottime->handle_protocol,
+@@ -285,19 +402,21 @@
+ if (status != EFI_SUCCESS)
+ continue;
+
+- efi_call_phys3(sys_table->boottime->handle_protocol,
+- h, &pciio_proto, &pciio);
++ status = efi_call_phys3(sys_table->boottime->handle_protocol,
++ h, &conout_proto, &dummy);
++
++ if (status == EFI_SUCCESS)
++ conout_found = true;
+
+ status = efi_call_phys4(gop->query_mode, gop,
+ gop->mode->mode, &size, &info);
+- if (status == EFI_SUCCESS && (!first_gop || pciio)) {
++ if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
+ /*
+- * Apple provide GOPs that are not backed by
+- * real hardware (they're used to handle
+- * multiple displays). The workaround is to
+- * search for a GOP implementing the PCIIO
+- * protocol, and if one isn't found, to just
+- * fallback to the first GOP.
++ * Systems that use the UEFI Console Splitter may
++ * provide multiple GOP devices, not all of which are
++ * backed by real hardware. The workaround is to search
++ * for a GOP implementing the ConOut protocol, and if
++ * one isn't found, to just fall back to the first GOP.
+ */
+ width = info->horizontal_resolution;
+ height = info->vertical_resolution;
+@@ -308,10 +427,10 @@
+ pixels_per_scan_line = info->pixels_per_scan_line;
+
+ /*
+- * Once we've found a GOP supporting PCIIO,
++ * Once we've found a GOP supporting ConOut,
+ * don't bother looking any further.
+ */
+- if (pciio)
++ if (conout_found)
+ break;
+
+ first_gop = gop;
+@@ -1052,6 +1171,8 @@
+
+ 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 -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/boot/compressed/eboot.h ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/boot/compressed/eboot.h
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/boot/compressed/eboot.h 2012-07-21 16:58:29.000000000 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/boot/compressed/eboot.h 2012-08-22 15:25:40.530244882 -0400
+@@ -14,6 +14,10 @@
+ #define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT)
+ #define EFI_READ_CHUNK_SIZE (1024 * 1024)
+
++#define EFI_CONSOLE_OUT_DEVICE_GUID \
++ EFI_GUID( 0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \
++ 0x3f, 0xc1, 0x4d )
++
+ #define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0
+ #define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1
+ #define PIXEL_BIT_MASK 2
+diff -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/include/asm/bootparam.h ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/include/asm/bootparam.h
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/include/asm/bootparam.h 2012-08-22 15:26:32.485522068 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/include/asm/bootparam.h 2012-08-22 15:25:40.530244882 -0400
+@@ -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 -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/include/asm/pci.h ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/include/asm/pci.h
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/include/asm/pci.h 2012-07-21 16:58:29.000000000 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/include/asm/pci.h 2012-08-22 15:25:40.530244882 -0400
+@@ -171,4 +171,16 @@
+ }
+ #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 -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/pci/common.c ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/pci/common.c
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/pci/common.c 2012-08-22 15:24:45.477951182 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/arch/x86/pci/common.c 2012-08-22 15:25:40.530244882 -0400
+@@ -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,38 @@
+ 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;
++
++ if (boot_params.hdr.version < 0x0209)
++ return 0;
++
++ 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 *)(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 -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/bus.c ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/bus.c
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/bus.c 2012-08-22 15:24:47.425961575 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/bus.c 2012-08-22 15:26:20.147456241 -0400
+@@ -166,6 +166,11 @@
+ 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 -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/pci.c ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/pci.c
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/pci.c 2012-08-22 15:24:47.432961612 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/pci.c 2012-08-22 15:25:40.531244893 -0400
+@@ -1385,6 +1385,19 @@
+ 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 __attribute__ ((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 -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/rom.c ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/rom.c
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/rom.c 2012-07-21 16:58:29.000000000 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/drivers/pci/rom.c 2012-08-22 15:25:40.531244893 -0400
+@@ -126,6 +126,12 @@
+ /* primary video rom always starts here */
+ start = (loff_t)0xC0000;
+ *size = 0x20000; /* cover C000:0 through E000:0 */
++ /*
++ * Some devices may provide ROMs via a source other than the BAR
++ */
++ } else if (pdev->rom && pdev->romlen) {
++ *size = pdev->romlen;
++ return phys_to_virt(pdev->rom);
+ } else {
+ if (res->flags &
+ (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY)) {
+@@ -219,7 +225,8 @@
+ 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 -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/include/linux/efi.h ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/include/linux/efi.h
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/include/linux/efi.h 2012-08-22 15:24:49.550972911 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/include/linux/efi.h 2012-08-22 15:25:40.533244906 -0400
+@@ -196,6 +196,77 @@
+ 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 -ur linux-3.6.0-0.rc2.git2.1.fc18.x86_64/include/linux/pci.h ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/include/linux/pci.h
+--- linux-3.6.0-0.rc2.git2.1.fc18.x86_64/include/linux/pci.h 2012-08-22 15:24:48.703968392 -0400
++++ ../kernel-3.5.fc18.bak/linux-3.6.0-0.rc2.git2.1.fc18.x86_64/include/linux/pci.h 2012-08-22 15:25:40.534244910 -0400
+@@ -355,6 +355,8 @@
+ };
+ 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)
+@@ -1582,6 +1584,7 @@
+ 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);