aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/efi
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@suse.com>2013-01-17 10:55:00 +0100
committerJan Beulich <jbeulich@suse.com>2013-01-17 10:55:00 +0100
commit39bc4673785f6455242f514ea0fa9359bdf1639c (patch)
tree4f263b1dba97b54337f513d7239c29b13a3bd740 /xen/arch/x86/efi
parentd56d7e35b74482b18d5f3562a368a2c7a238b5f2 (diff)
downloadxen-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.c91
-rw-r--r--xen/arch/x86/efi/efi.h10
-rw-r--r--xen/arch/x86/efi/runtime.c25
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;
}