diff options
author | Jan Beulich <jbeulich@suse.com> | 2013-01-17 10:55:00 +0100 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2013-01-17 10:55:00 +0100 |
commit | 39bc4673785f6455242f514ea0fa9359bdf1639c (patch) | |
tree | 4f263b1dba97b54337f513d7239c29b13a3bd740 /xen/arch/x86/efi | |
parent | d56d7e35b74482b18d5f3562a368a2c7a238b5f2 (diff) | |
download | xen-39bc4673785f6455242f514ea0fa9359bdf1639c.tar.gz xen-39bc4673785f6455242f514ea0fa9359bdf1639c.tar.bz2 xen-39bc4673785f6455242f514ea0fa9359bdf1639c.zip |
x86/EFI: retrieve PCI ROM contents not accessible through BARs
Linux 3.8-rc added code to do this, so we need to support this in the
hypervisor for Dom0 to be able to get at the same information as a
native kernel.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
Diffstat (limited to 'xen/arch/x86/efi')
-rw-r--r-- | xen/arch/x86/efi/boot.c | 91 | ||||
-rw-r--r-- | xen/arch/x86/efi/efi.h | 10 | ||||
-rw-r--r-- | xen/arch/x86/efi/runtime.c | 25 |
3 files changed, 126 insertions, 0 deletions
diff --git a/xen/arch/x86/efi/boot.c b/xen/arch/x86/efi/boot.c index 10c698c090..725b52c3c7 100644 --- a/xen/arch/x86/efi/boot.c +++ b/xen/arch/x86/efi/boot.c @@ -1,5 +1,6 @@ #include "efi.h" #include <efi/efiprot.h> +#include <efi/efipciio.h> #include <public/xen.h> #include <xen/compile.h> #include <xen/ctype.h> @@ -8,6 +9,7 @@ #include <xen/keyhandler.h> #include <xen/lib.h> #include <xen/multiboot.h> +#include <xen/pci_regs.h> #include <xen/pfn.h> #if EFI_PAGE_SIZE != PAGE_SIZE # error Cannot use xen/pfn.h here! @@ -570,6 +572,92 @@ static void __init edd_put_string(u8 *dst, size_t n, const char *src) } #define edd_put_string(d, s) edd_put_string(d, ARRAY_SIZE(d), s) +static void __init setup_efi_pci(void) +{ + EFI_STATUS status; + EFI_HANDLE *handles; + static EFI_GUID __initdata pci_guid = EFI_PCI_IO_PROTOCOL; + UINTN i, nr_pci, size = 0; + struct efi_pci_rom *last = NULL; + + status = efi_bs->LocateHandle(ByProtocol, &pci_guid, NULL, &size, NULL); + if ( status == EFI_BUFFER_TOO_SMALL ) + status = efi_bs->AllocatePool(EfiLoaderData, size, (void **)&handles); + if ( !EFI_ERROR(status) ) + status = efi_bs->LocateHandle(ByProtocol, &pci_guid, NULL, &size, + handles); + if ( EFI_ERROR(status) ) + size = 0; + + nr_pci = size / sizeof(*handles); + for ( i = 0; i < nr_pci; ++i ) + { + EFI_PCI_IO *pci = NULL; + u64 attributes; + struct efi_pci_rom *rom, *va; + UINTN segment, bus, device, function; + + status = efi_bs->HandleProtocol(handles[i], &pci_guid, (void **)&pci); + if ( EFI_ERROR(status) || !pci || !pci->RomImage || !pci->RomSize ) + continue; + + status = pci->Attributes(pci, EfiPciIoAttributeOperationGet, 0, + &attributes); + if ( EFI_ERROR(status) || + !(attributes & EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM) || + EFI_ERROR(pci->GetLocation(pci, &segment, &bus, &device, + &function)) ) + continue; + + DisplayUint(segment, 4); + PrintStr(L":"); + DisplayUint(bus, 2); + PrintStr(L":"); + DisplayUint(device, 2); + PrintStr(L"."); + DisplayUint(function, 1); + PrintStr(L": ROM: "); + DisplayUint(pci->RomSize, 0); + PrintStr(L" bytes at "); + DisplayUint((UINTN)pci->RomImage, 0); + PrintStr(newline); + + size = pci->RomSize + sizeof(*rom); + status = efi_bs->AllocatePool(EfiRuntimeServicesData, size, + (void **)&rom); + if ( EFI_ERROR(status) ) + continue; + + rom->next = NULL; + rom->size = pci->RomSize; + + status = pci->Pci.Read(pci, EfiPciIoWidthUint16, PCI_VENDOR_ID, 1, + &rom->vendor); + if ( !EFI_ERROR(status) ) + status = pci->Pci.Read(pci, EfiPciIoWidthUint16, PCI_DEVICE_ID, 1, + &rom->devid); + if ( EFI_ERROR(status) ) + { + efi_bs->FreePool(rom); + continue; + } + + rom->segment = segment; + rom->bus = bus; + rom->devfn = (device << 3) | function; + memcpy(rom->data, pci->RomImage, pci->RomSize); + + va = (void *)rom + DIRECTMAP_VIRT_START; + if ( last ) + last->next = va; + else + efi_pci_roms = va; + last = rom; + } + + efi_bs->FreePool(handles); +} + static int __init set_color(u32 mask, int bpp, u8 *pos, u8 *sz) { if ( bpp < 0 ) @@ -1140,6 +1228,9 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) if (efi.smbios != EFI_INVALID_TABLE_ADDR) dmi_efi_get_table((void *)(long)efi.smbios); + /* Collect PCI ROM contents. */ + setup_efi_pci(); + /* Allocate space for trampoline (in first Mb). */ cfg.addr = 0x100000; cfg.size = trampoline_end - trampoline_start; diff --git a/xen/arch/x86/efi/efi.h b/xen/arch/x86/efi/efi.h index 561434e14c..43dbd1728b 100644 --- a/xen/arch/x86/efi/efi.h +++ b/xen/arch/x86/efi/efi.h @@ -9,6 +9,14 @@ #include <xen/spinlock.h> #include <asm/page.h> +struct efi_pci_rom { + const struct efi_pci_rom *next; + u16 vendor, devid, segment; + u8 bus, devfn; + unsigned long size; + unsigned char data[]; +}; + extern unsigned int efi_num_ct; extern EFI_CONFIGURATION_TABLE *efi_ct; @@ -22,5 +30,7 @@ extern void *efi_memmap; extern l4_pgentry_t *efi_l4_pgtable; +extern const struct efi_pci_rom *efi_pci_roms; + unsigned long efi_rs_enter(void); void efi_rs_leave(unsigned long); diff --git a/xen/arch/x86/efi/runtime.c b/xen/arch/x86/efi/runtime.c index b2ff495720..c5b03bfe2c 100644 --- a/xen/arch/x86/efi/runtime.c +++ b/xen/arch/x86/efi/runtime.c @@ -37,6 +37,8 @@ struct efi __read_mostly efi = { l4_pgentry_t *__read_mostly efi_l4_pgtable; +const struct efi_pci_rom *__read_mostly efi_pci_roms; + unsigned long efi_rs_enter(void) { unsigned long cr3 = read_cr3(); @@ -177,6 +179,29 @@ int efi_get_info(uint32_t idx, union xenpf_efi_info *info) } } return -ESRCH; + case XEN_FW_EFI_PCI_ROM: { + const struct efi_pci_rom *ent; + + for ( ent = efi_pci_roms; ent; ent = ent->next ) + if ( info->pci_rom.segment == ent->segment && + info->pci_rom.bus == ent->bus && + info->pci_rom.devfn == ent->devfn && + info->pci_rom.vendor == ent->vendor && + info->pci_rom.devid == ent->devid ) + { + int rc = 0; + + if ( info->pci_rom.size < ent->size ) + rc = -ENOSPC; + else if ( copy_to_guest(info->pci_rom.data, + ent->data, ent->size) ) + rc = -EFAULT; + info->pci_rom.size = ent->size; + + return rc; + } + return -ESRCH; + } default: return -EINVAL; } |