diff options
author | Matthew Garrett <mjg@redhat.com> | 2012-09-04 11:31:54 -0400 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2012-09-04 11:35:16 -0400 |
commit | 447e3a6aa1760b6d235df50e7ba8570902ede270 (patch) | |
tree | ee7b776d8dc88de068461c148ef8ee16b33c5499 /handle-efi-roms.patch | |
parent | 1b72b444f0ff50136c819be540b67ecddbf815df (diff) | |
download | kernel-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.patch | 458 |
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); |