diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c
index 14e2f46..ca604be 100644
--- a/xen/common/efi/boot.c
+++ b/xen/common/efi/boot.c
@@ -18,16 +18,6 @@
 #include <xen/string.h>
 #include <xen/stringify.h>
 #include <xen/vga.h>
-#include <asm/e820.h>
-#include <asm/edd.h>
-#define __ASSEMBLY__ /* avoid pulling in ACPI stuff (conflicts with EFI) */
-#include <asm/fixmap.h>
-#undef __ASSEMBLY__
-#include <asm/msr.h>
-#include <asm/processor.h>
-
-/* Using SetVirtualAddressMap() is incompatible with kexec: */
-#undef USE_SET_VIRTUAL_ADDRESS_MAP
 
 #define SHIM_LOCK_PROTOCOL_GUID \
   { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }
@@ -41,8 +31,10 @@ typedef struct {
     EFI_SHIM_LOCK_VERIFY Verify;
 } EFI_SHIM_LOCK_PROTOCOL;
 
-extern char start[];
-extern u32 cpuid_ext_features;
+static CHAR16 *__init FormatDec(UINT64 Val, CHAR16 *Buffer);
+static CHAR16 *__init FormatHex(UINT64 Val, UINTN Width, CHAR16 *Buffer);
+static void __init DisplayUint(UINT64 Val, INTN Width);
+static CHAR16 *__init wstrcpy(CHAR16 *d, const CHAR16 *s);
 
 union string {
     CHAR16 *w;
@@ -69,19 +61,18 @@ static UINT32 __initdata mdesc_ver;
 static struct file __initdata cfg;
 static struct file __initdata kernel;
 static struct file __initdata ramdisk;
-static struct file __initdata ucode;
 static struct file __initdata xsm;
-
-static multiboot_info_t __initdata mbi = {
-    .flags = MBI_MODULES | MBI_LOADERNAME
-};
-static module_t __initdata mb_modules[3];
-
 static CHAR16 __initdata newline[] = L"\r\n";
 
 #define PrintStr(s) StdOut->OutputString(StdOut, s)
 #define PrintErr(s) StdErr->OutputString(StdErr, s)
 
+/*
+ * Include architecture specific implementation here, which references the
+ * static globals defined above.
+ */
+#include <asm/efi-boot.h>
+
 static CHAR16 *__init FormatDec(UINT64 Val, CHAR16 *Buffer)
 {
     if ( Val >= 10 )
@@ -255,32 +246,6 @@ static void __init PrintErrMesg(const CHAR16 *mesg, EFI_STATUS ErrCode)
     blexit(mesg);
 }
 
-static void __init place_string(u32 *addr, const char *s)
-{
-    static char *__initdata alloc = start;
-
-    if ( s && *s )
-    {
-        size_t len1 = strlen(s) + 1;
-        const char *old = (char *)(long)*addr;
-        size_t len2 = *addr ? strlen(old) + 1 : 0;
-
-        alloc -= len1 + len2;
-        /*
-         * Insert new string before already existing one. This is needed
-         * for options passed on the command line to override options from
-         * the configuration file.
-         */
-        memcpy(alloc, s, len1);
-        if ( *addr )
-        {
-            alloc[len1 - 1] = ' ';
-            memcpy(alloc + len1, old, len2);
-        }
-    }
-    *addr = (long)alloc;
-}
-
 static unsigned int __init get_argv(unsigned int argc, CHAR16 **argv,
                                     CHAR16 *cmdline, UINTN cmdsize)
 {
@@ -574,104 +539,6 @@ static void __init split_value(char *s)
     *s = 0;
 }
 
-static void __init edd_put_string(u8 *dst, size_t n, const char *src)
-{
-    while ( n-- && *src )
-       *dst++ = *src++;
-    if ( *src )
-       PrintErrMesg(L"Internal error populating EDD info",
-                    EFI_BUFFER_TOO_SMALL);
-    while ( n-- )
-       *dst++ = ' ';
-}
-#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 )
@@ -687,82 +554,6 @@ static int __init set_color(u32 mask, int bpp, u8 *pos, u8 *sz)
    return max(*pos + *sz, bpp);
 }
 
-extern const intpte_t __page_tables_start[], __page_tables_end[];
-#define in_page_tables(v) ((intpte_t *)(v) >= __page_tables_start && \
-                           (intpte_t *)(v) < __page_tables_end)
-
-#define PE_BASE_RELOC_ABS      0
-#define PE_BASE_RELOC_HIGHLOW  3
-#define PE_BASE_RELOC_DIR64   10
-
-extern const struct pe_base_relocs {
-    u32 rva;
-    u32 size;
-    u16 entries[];
-} __base_relocs_start[], __base_relocs_end[];
-
-static void __init relocate_image(unsigned long delta)
-{
-    const struct pe_base_relocs *base_relocs;
-
-    for ( base_relocs = __base_relocs_start; base_relocs < __base_relocs_end; )
-    {
-        unsigned int i, n;
-
-        n = (base_relocs->size - sizeof(*base_relocs)) /
-            sizeof(*base_relocs->entries);
-        for ( i = 0; i < n; ++i )
-        {
-            unsigned long addr = xen_phys_start + base_relocs->rva +
-                                 (base_relocs->entries[i] & 0xfff);
-
-            switch ( base_relocs->entries[i] >> 12 )
-            {
-            case PE_BASE_RELOC_ABS:
-                break;
-            case PE_BASE_RELOC_HIGHLOW:
-                if ( delta )
-                {
-                    *(u32 *)addr += delta;
-                    if ( in_page_tables(addr) )
-                        *(u32 *)addr += xen_phys_start;
-                }
-                break;
-            case PE_BASE_RELOC_DIR64:
-                if ( delta )
-                {
-                    *(u64 *)addr += delta;
-                    if ( in_page_tables(addr) )
-                        *(intpte_t *)addr += xen_phys_start;
-                }
-                break;
-            default:
-                blexit(L"Unsupported relocation type");
-            }
-        }
-        base_relocs = (const void *)(base_relocs->entries + i + (i & 1));
-    }
-}
-
-extern const s32 __trampoline_rel_start[], __trampoline_rel_stop[];
-extern const s32 __trampoline_seg_start[], __trampoline_seg_stop[];
-
-static void __init relocate_trampoline(unsigned long phys)
-{
-    const s32 *trampoline_ptr;
-
-    trampoline_phys = phys;
-    /* Apply relocations to trampoline. */
-    for ( trampoline_ptr = __trampoline_rel_start;
-          trampoline_ptr < __trampoline_rel_stop;
-          ++trampoline_ptr )
-        *(u32 *)(*trampoline_ptr + (long)trampoline_ptr) += phys;
-    for ( trampoline_ptr = __trampoline_seg_start;
-          trampoline_ptr < __trampoline_seg_stop;
-          ++trampoline_ptr )
-        *(u16 *)(*trampoline_ptr + (long)trampoline_ptr) = phys >> 4;
-}
-
 void EFIAPI __init noreturn
 efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
 {
@@ -879,7 +670,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     PrintStr(L"Xen " __stringify(XEN_VERSION) "." __stringify(XEN_SUBVERSION)
              XEN_EXTRAVERSION " (c/s " XEN_CHANGESET ") EFI loader\r\n");
 
-    relocate_image(0);
+    efi_arch_relocate_image(0);
 
     if ( StdOut->QueryMode(StdOut, StdOut->Mode->Mode,
                            &cols, &rows) == EFI_SUCCESS )
@@ -1258,7 +1049,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
         dmi_efi_get_table((void *)(long)efi.smbios);
 
     /* Collect PCI ROM contents. */
-    setup_efi_pci();
+    efi_arch_pci();
 
     /* Get snapshot of variable store parameters. */
     status = (efi_rs->Hdr.Revision >> 16) >= 2 ?
@@ -1460,7 +1251,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     efi_memmap = (void *)efi_memmap + DIRECTMAP_VIRT_START;
     efi_fw_vendor = (void *)efi_fw_vendor + DIRECTMAP_VIRT_START;
 
-    relocate_image(__XEN_VIRT_START - xen_phys_start);
+    efi_arch_relocate_image(__XEN_VIRT_START - xen_phys_start);
     memcpy((void *)trampoline_phys, trampoline_start, cfg.size);
 
     /* Set system registers and transfer control. */
@@ -1496,228 +1287,3 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
     for( ; ; ); /* not reached */
 }
 
-#ifndef USE_SET_VIRTUAL_ADDRESS_MAP
-static __init void copy_mapping(unsigned long mfn, unsigned long end,
-                                bool_t (*is_valid)(unsigned long smfn,
-                                                   unsigned long emfn))
-{
-    unsigned long next;
-
-    for ( ; mfn < end; mfn = next )
-    {
-        l4_pgentry_t l4e = efi_l4_pgtable[l4_table_offset(mfn << PAGE_SHIFT)];
-        l3_pgentry_t *l3src, *l3dst;
-        unsigned long va = (unsigned long)mfn_to_virt(mfn);
-
-        next = mfn + (1UL << (L3_PAGETABLE_SHIFT - PAGE_SHIFT));
-        if ( !is_valid(mfn, min(next, end)) )
-            continue;
-        if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) )
-        {
-            l3dst = alloc_xen_pagetable();
-            BUG_ON(!l3dst);
-            clear_page(l3dst);
-            efi_l4_pgtable[l4_table_offset(mfn << PAGE_SHIFT)] =
-                l4e_from_paddr(virt_to_maddr(l3dst), __PAGE_HYPERVISOR);
-        }
-        else
-            l3dst = l4e_to_l3e(l4e);
-        l3src = l4e_to_l3e(idle_pg_table[l4_table_offset(va)]);
-        l3dst[l3_table_offset(mfn << PAGE_SHIFT)] = l3src[l3_table_offset(va)];
-    }
-}
-
-static bool_t __init ram_range_valid(unsigned long smfn, unsigned long emfn)
-{
-    unsigned long sz = pfn_to_pdx(emfn - 1) / PDX_GROUP_COUNT + 1;
-
-    return !(smfn & pfn_hole_mask) &&
-           find_next_bit(pdx_group_valid, sz,
-                         pfn_to_pdx(smfn) / PDX_GROUP_COUNT) < sz;
-}
-
-static bool_t __init rt_range_valid(unsigned long smfn, unsigned long emfn)
-{
-    return 1;
-}
-#endif
-
-#define INVALID_VIRTUAL_ADDRESS (0xBAAADUL << \
-                                 (EFI_PAGE_SHIFT + BITS_PER_LONG - 32))
-
-void __init efi_init_memory(void)
-{
-    unsigned int i;
-#ifndef USE_SET_VIRTUAL_ADDRESS_MAP
-    struct rt_extra {
-        struct rt_extra *next;
-        unsigned long smfn, emfn;
-        unsigned int prot;
-    } *extra, *extra_head = NULL;
-#endif
-
-    printk(XENLOG_INFO "EFI memory map:\n");
-    for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size )
-    {
-        EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;
-        u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT;
-        unsigned long smfn, emfn;
-        unsigned int prot = PAGE_HYPERVISOR;
-
-        printk(XENLOG_INFO " %013" PRIx64 "-%013" PRIx64
-                           " type=%u attr=%016" PRIx64 "\n",
-               desc->PhysicalStart, desc->PhysicalStart + len - 1,
-               desc->Type, desc->Attribute);
-
-        if ( !(desc->Attribute & EFI_MEMORY_RUNTIME) )
-            continue;
-
-        desc->VirtualStart = INVALID_VIRTUAL_ADDRESS;
-
-        smfn = PFN_DOWN(desc->PhysicalStart);
-        emfn = PFN_UP(desc->PhysicalStart + len);
-
-        if ( desc->Attribute & EFI_MEMORY_WB )
-            /* nothing */;
-        else if ( desc->Attribute & EFI_MEMORY_WT )
-            prot |= _PAGE_PWT | MAP_SMALL_PAGES;
-        else if ( desc->Attribute & EFI_MEMORY_WC )
-            prot |= _PAGE_PAT | MAP_SMALL_PAGES;
-        else if ( desc->Attribute & (EFI_MEMORY_UC | EFI_MEMORY_UCE) )
-            prot |= _PAGE_PWT | _PAGE_PCD | MAP_SMALL_PAGES;
-        else
-        {
-            printk(XENLOG_ERR "Unknown cachability for MFNs %#lx-%#lx\n",
-                   smfn, emfn - 1);
-            continue;
-        }
-
-        if ( desc->Attribute & EFI_MEMORY_WP )
-            prot &= _PAGE_RW;
-        if ( desc->Attribute & EFI_MEMORY_XP )
-            prot |= _PAGE_NX_BIT;
-
-        if ( pfn_to_pdx(emfn - 1) < (DIRECTMAP_SIZE >> PAGE_SHIFT) &&
-             !(smfn & pfn_hole_mask) &&
-             !((smfn ^ (emfn - 1)) & ~pfn_pdx_bottom_mask) )
-        {
-            if ( (unsigned long)mfn_to_virt(emfn - 1) >= HYPERVISOR_VIRT_END )
-                prot &= ~_PAGE_GLOBAL;
-            if ( map_pages_to_xen((unsigned long)mfn_to_virt(smfn),
-                                  smfn, emfn - smfn, prot) == 0 )
-                desc->VirtualStart =
-                    (unsigned long)maddr_to_virt(desc->PhysicalStart);
-            else
-                printk(XENLOG_ERR "Could not map MFNs %#lx-%#lx\n",
-                       smfn, emfn - 1);
-        }
-#ifndef USE_SET_VIRTUAL_ADDRESS_MAP
-        else if ( !((desc->PhysicalStart + len - 1) >> (VADDR_BITS - 1)) &&
-                  (extra = xmalloc(struct rt_extra)) != NULL )
-        {
-            extra->smfn = smfn;
-            extra->emfn = emfn;
-            extra->prot = prot & ~_PAGE_GLOBAL;
-            extra->next = extra_head;
-            extra_head = extra;
-            desc->VirtualStart = desc->PhysicalStart;
-        }
-#endif
-        else
-        {
-#ifdef USE_SET_VIRTUAL_ADDRESS_MAP
-            /* XXX allocate e.g. down from FIXADDR_START */
-#endif
-            printk(XENLOG_ERR "No mapping for MFNs %#lx-%#lx\n",
-                   smfn, emfn - 1);
-        }
-    }
-
-#ifdef USE_SET_VIRTUAL_ADDRESS_MAP
-    efi_rs->SetVirtualAddressMap(efi_memmap_size, efi_mdesc_size,
-                                 mdesc_ver, efi_memmap);
-#else
-    /* Set up 1:1 page tables to do runtime calls in "physical" mode. */
-    efi_l4_pgtable = alloc_xen_pagetable();
-    BUG_ON(!efi_l4_pgtable);
-    clear_page(efi_l4_pgtable);
-
-    copy_mapping(0, max_page, ram_range_valid);
-
-    /* Insert non-RAM runtime mappings inside the direct map. */
-    for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size )
-    {
-        const EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;
-
-        if ( (desc->Attribute & EFI_MEMORY_RUNTIME) &&
-             desc->VirtualStart != INVALID_VIRTUAL_ADDRESS &&
-             desc->VirtualStart != desc->PhysicalStart )
-            copy_mapping(PFN_DOWN(desc->PhysicalStart),
-                         PFN_UP(desc->PhysicalStart +
-                                (desc->NumberOfPages << EFI_PAGE_SHIFT)),
-                         rt_range_valid);
-    }
-
-    /* Insert non-RAM runtime mappings outside of the direct map. */
-    while ( (extra = extra_head) != NULL )
-    {
-        unsigned long addr = extra->smfn << PAGE_SHIFT;
-        l4_pgentry_t l4e = efi_l4_pgtable[l4_table_offset(addr)];
-        l3_pgentry_t *pl3e;
-        l2_pgentry_t *pl2e;
-        l1_pgentry_t *l1t;
-
-        if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) )
-        {
-            pl3e = alloc_xen_pagetable();
-            BUG_ON(!pl3e);
-            clear_page(pl3e);
-            efi_l4_pgtable[l4_table_offset(addr)] =
-                l4e_from_paddr(virt_to_maddr(pl3e), __PAGE_HYPERVISOR);
-        }
-        else
-            pl3e = l4e_to_l3e(l4e);
-        pl3e += l3_table_offset(addr);
-        if ( !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) )
-        {
-            pl2e = alloc_xen_pagetable();
-            BUG_ON(!pl2e);
-            clear_page(pl2e);
-            *pl3e = l3e_from_paddr(virt_to_maddr(pl2e), __PAGE_HYPERVISOR);
-        }
-        else
-        {
-            BUG_ON(l3e_get_flags(*pl3e) & _PAGE_PSE);
-            pl2e = l3e_to_l2e(*pl3e);
-        }
-        pl2e += l2_table_offset(addr);
-        if ( !(l2e_get_flags(*pl2e) & _PAGE_PRESENT) )
-        {
-            l1t = alloc_xen_pagetable();
-            BUG_ON(!l1t);
-            clear_page(l1t);
-            *pl2e = l2e_from_paddr(virt_to_maddr(l1t), __PAGE_HYPERVISOR);
-        }
-        else
-        {
-            BUG_ON(l2e_get_flags(*pl2e) & _PAGE_PSE);
-            l1t = l2e_to_l1e(*pl2e);
-        }
-        for ( i = l1_table_offset(addr);
-              i < L1_PAGETABLE_ENTRIES && extra->smfn < extra->emfn;
-              ++i, ++extra->smfn )
-            l1t[i] = l1e_from_pfn(extra->smfn, extra->prot);
-
-        if ( extra->smfn == extra->emfn )
-        {
-            extra_head = extra->next;
-            xfree(extra);
-        }
-    }
-
-    /* Insert Xen mappings. */
-    for ( i = l4_table_offset(HYPERVISOR_VIRT_START);
-          i < l4_table_offset(DIRECTMAP_VIRT_END); ++i )
-        efi_l4_pgtable[i] = idle_pg_table[i];
-#endif
-}
diff --git a/xen/include/asm-x86/efi-boot.h b/xen/include/asm-x86/efi-boot.h
new file mode 100644
index 0000000..5b91697
--- /dev/null
+++ b/xen/include/asm-x86/efi-boot.h
@@ -0,0 +1,455 @@
+/*
+ * Architecture specific implementation for EFI boot code.  This file
+ * is intended to be included by XXX _only_, and therefore can define
+ * arch specific global variables.
+ */
+#include <asm/e820.h>
+#include <asm/edd.h>
+#define __ASSEMBLY__ /* avoid pulling in ACPI stuff (conflicts with EFI) */
+#include <asm/fixmap.h>
+#undef __ASSEMBLY__
+#include <asm/msr.h>
+#include <asm/processor.h>
+
+static struct file __initdata ucode;
+static multiboot_info_t __initdata mbi = {
+    .flags = MBI_MODULES | MBI_LOADERNAME
+};
+static module_t __initdata mb_modules[3];
+
+static void noreturn blexit(const CHAR16 *str);
+static void PrintErrMesg(const CHAR16 *mesg, EFI_STATUS ErrCode);
+
+/* Using SetVirtualAddressMap() is incompatible with kexec: */
+#undef USE_SET_VIRTUAL_ADDRESS_MAP
+extern char start[];
+extern u32 cpuid_ext_features;
+
+static void __init edd_put_string(u8 *dst, size_t n, const char *src)
+{
+    while ( n-- && *src )
+       *dst++ = *src++;
+    if ( *src )
+       PrintErrMesg(L"Internal error populating EDD info",
+                    EFI_BUFFER_TOO_SMALL);
+    while ( n-- )
+       *dst++ = ' ';
+}
+#define edd_put_string(d, s) edd_put_string(d, ARRAY_SIZE(d), s)
+
+static void __init efi_arch_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);
+}
+
+
+extern const intpte_t __page_tables_start[], __page_tables_end[];
+#define in_page_tables(v) ((intpte_t *)(v) >= __page_tables_start && \
+                           (intpte_t *)(v) < __page_tables_end)
+
+#define PE_BASE_RELOC_ABS      0
+#define PE_BASE_RELOC_HIGHLOW  3
+#define PE_BASE_RELOC_DIR64   10
+
+extern const struct pe_base_relocs {
+    u32 rva;
+    u32 size;
+    u16 entries[];
+} __base_relocs_start[], __base_relocs_end[];
+
+static void __init efi_arch_relocate_image(unsigned long delta)
+{
+    const struct pe_base_relocs *base_relocs;
+
+    for ( base_relocs = __base_relocs_start; base_relocs < __base_relocs_end; )
+    {
+        unsigned int i, n;
+
+        n = (base_relocs->size - sizeof(*base_relocs)) /
+            sizeof(*base_relocs->entries);
+        for ( i = 0; i < n; ++i )
+        {
+            unsigned long addr = xen_phys_start + base_relocs->rva +
+                                 (base_relocs->entries[i] & 0xfff);
+
+            switch ( base_relocs->entries[i] >> 12 )
+            {
+            case PE_BASE_RELOC_ABS:
+                break;
+            case PE_BASE_RELOC_HIGHLOW:
+                if ( delta )
+                {
+                    *(u32 *)addr += delta;
+                    if ( in_page_tables(addr) )
+                        *(u32 *)addr += xen_phys_start;
+                }
+                break;
+            case PE_BASE_RELOC_DIR64:
+                if ( delta )
+                {
+                    *(u64 *)addr += delta;
+                    if ( in_page_tables(addr) )
+                        *(intpte_t *)addr += xen_phys_start;
+                }
+                break;
+            default:
+                blexit(L"Unsupported relocation type");
+            }
+        }
+        base_relocs = (const void *)(base_relocs->entries + i + (i & 1));
+    }
+}
+
+extern const s32 __trampoline_rel_start[], __trampoline_rel_stop[];
+extern const s32 __trampoline_seg_start[], __trampoline_seg_stop[];
+
+static void __init relocate_trampoline(unsigned long phys)
+{
+    const s32 *trampoline_ptr;
+
+    trampoline_phys = phys;
+    /* Apply relocations to trampoline. */
+    for ( trampoline_ptr = __trampoline_rel_start;
+          trampoline_ptr < __trampoline_rel_stop;
+          ++trampoline_ptr )
+        *(u32 *)(*trampoline_ptr + (long)trampoline_ptr) += phys;
+    for ( trampoline_ptr = __trampoline_seg_start;
+          trampoline_ptr < __trampoline_seg_stop;
+          ++trampoline_ptr )
+        *(u16 *)(*trampoline_ptr + (long)trampoline_ptr) = phys >> 4;
+}
+
+
+#ifndef USE_SET_VIRTUAL_ADDRESS_MAP
+static __init void copy_mapping(unsigned long mfn, unsigned long end,
+                                bool_t (*is_valid)(unsigned long smfn,
+                                                   unsigned long emfn))
+{
+    unsigned long next;
+
+    for ( ; mfn < end; mfn = next )
+    {
+        l4_pgentry_t l4e = efi_l4_pgtable[l4_table_offset(mfn << PAGE_SHIFT)];
+        l3_pgentry_t *l3src, *l3dst;
+        unsigned long va = (unsigned long)mfn_to_virt(mfn);
+
+        next = mfn + (1UL << (L3_PAGETABLE_SHIFT - PAGE_SHIFT));
+        if ( !is_valid(mfn, min(next, end)) )
+            continue;
+        if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) )
+        {
+            l3dst = alloc_xen_pagetable();
+            BUG_ON(!l3dst);
+            clear_page(l3dst);
+            efi_l4_pgtable[l4_table_offset(mfn << PAGE_SHIFT)] =
+                l4e_from_paddr(virt_to_maddr(l3dst), __PAGE_HYPERVISOR);
+        }
+        else
+            l3dst = l4e_to_l3e(l4e);
+        l3src = l4e_to_l3e(idle_pg_table[l4_table_offset(va)]);
+        l3dst[l3_table_offset(mfn << PAGE_SHIFT)] = l3src[l3_table_offset(va)];
+    }
+}
+
+static bool_t __init ram_range_valid(unsigned long smfn, unsigned long emfn)
+{
+    unsigned long sz = pfn_to_pdx(emfn - 1) / PDX_GROUP_COUNT + 1;
+
+    return !(smfn & pfn_hole_mask) &&
+           find_next_bit(pdx_group_valid, sz,
+                         pfn_to_pdx(smfn) / PDX_GROUP_COUNT) < sz;
+}
+
+static bool_t __init rt_range_valid(unsigned long smfn, unsigned long emfn)
+{
+    return 1;
+}
+#endif
+
+#define INVALID_VIRTUAL_ADDRESS (0xBAAADUL << \
+                                 (EFI_PAGE_SHIFT + BITS_PER_LONG - 32))
+
+void __init efi_init_memory(void)
+{
+    unsigned int i;
+#ifndef USE_SET_VIRTUAL_ADDRESS_MAP
+    struct rt_extra {
+        struct rt_extra *next;
+        unsigned long smfn, emfn;
+        unsigned int prot;
+    } *extra, *extra_head = NULL;
+#endif
+
+    printk(XENLOG_INFO "EFI memory map:\n");
+    for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size )
+    {
+        EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;
+        u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT;
+        unsigned long smfn, emfn;
+        unsigned int prot = PAGE_HYPERVISOR;
+
+        printk(XENLOG_INFO " %013" PRIx64 "-%013" PRIx64
+                           " type=%u attr=%016" PRIx64 "\n",
+               desc->PhysicalStart, desc->PhysicalStart + len - 1,
+               desc->Type, desc->Attribute);
+
+        if ( !(desc->Attribute & EFI_MEMORY_RUNTIME) )
+            continue;
+
+        desc->VirtualStart = INVALID_VIRTUAL_ADDRESS;
+
+        smfn = PFN_DOWN(desc->PhysicalStart);
+        emfn = PFN_UP(desc->PhysicalStart + len);
+
+        if ( desc->Attribute & EFI_MEMORY_WB )
+            /* nothing */;
+        else if ( desc->Attribute & EFI_MEMORY_WT )
+            prot |= _PAGE_PWT | MAP_SMALL_PAGES;
+        else if ( desc->Attribute & EFI_MEMORY_WC )
+            prot |= _PAGE_PAT | MAP_SMALL_PAGES;
+        else if ( desc->Attribute & (EFI_MEMORY_UC | EFI_MEMORY_UCE) )
+            prot |= _PAGE_PWT | _PAGE_PCD | MAP_SMALL_PAGES;
+        else
+        {
+            printk(XENLOG_ERR "Unknown cachability for MFNs %#lx-%#lx\n",
+                   smfn, emfn - 1);
+            continue;
+        }
+
+        if ( desc->Attribute & EFI_MEMORY_WP )
+            prot &= _PAGE_RW;
+        if ( desc->Attribute & EFI_MEMORY_XP )
+            prot |= _PAGE_NX_BIT;
+
+        if ( pfn_to_pdx(emfn - 1) < (DIRECTMAP_SIZE >> PAGE_SHIFT) &&
+             !(smfn & pfn_hole_mask) &&
+             !((smfn ^ (emfn - 1)) & ~pfn_pdx_bottom_mask) )
+        {
+            if ( (unsigned long)mfn_to_virt(emfn - 1) >= HYPERVISOR_VIRT_END )
+                prot &= ~_PAGE_GLOBAL;
+            if ( map_pages_to_xen((unsigned long)mfn_to_virt(smfn),
+                                  smfn, emfn - smfn, prot) == 0 )
+                desc->VirtualStart =
+                    (unsigned long)maddr_to_virt(desc->PhysicalStart);
+            else
+                printk(XENLOG_ERR "Could not map MFNs %#lx-%#lx\n",
+                       smfn, emfn - 1);
+        }
+#ifndef USE_SET_VIRTUAL_ADDRESS_MAP
+        else if ( !((desc->PhysicalStart + len - 1) >> (VADDR_BITS - 1)) &&
+                  (extra = xmalloc(struct rt_extra)) != NULL )
+        {
+            extra->smfn = smfn;
+            extra->emfn = emfn;
+            extra->prot = prot & ~_PAGE_GLOBAL;
+            extra->next = extra_head;
+            extra_head = extra;
+            desc->VirtualStart = desc->PhysicalStart;
+        }
+#endif
+        else
+        {
+#ifdef USE_SET_VIRTUAL_ADDRESS_MAP
+            /* XXX allocate e.g. down from FIXADDR_START */
+#endif
+            printk(XENLOG_ERR "No mapping for MFNs %#lx-%#lx\n",
+                   smfn, emfn - 1);
+        }
+    }
+
+#ifdef USE_SET_VIRTUAL_ADDRESS_MAP
+    efi_rs->SetVirtualAddressMap(efi_memmap_size, efi_mdesc_size,
+                                 mdesc_ver, efi_memmap);
+#else
+    /* Set up 1:1 page tables to do runtime calls in "physical" mode. */
+    efi_l4_pgtable = alloc_xen_pagetable();
+    BUG_ON(!efi_l4_pgtable);
+    clear_page(efi_l4_pgtable);
+
+    copy_mapping(0, max_page, ram_range_valid);
+
+    /* Insert non-RAM runtime mappings inside the direct map. */
+    for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size )
+    {
+        const EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;
+
+        if ( (desc->Attribute & EFI_MEMORY_RUNTIME) &&
+             desc->VirtualStart != INVALID_VIRTUAL_ADDRESS &&
+             desc->VirtualStart != desc->PhysicalStart )
+            copy_mapping(PFN_DOWN(desc->PhysicalStart),
+                         PFN_UP(desc->PhysicalStart +
+                                (desc->NumberOfPages << EFI_PAGE_SHIFT)),
+                         rt_range_valid);
+    }
+
+    /* Insert non-RAM runtime mappings outside of the direct map. */
+    while ( (extra = extra_head) != NULL )
+    {
+        unsigned long addr = extra->smfn << PAGE_SHIFT;
+        l4_pgentry_t l4e = efi_l4_pgtable[l4_table_offset(addr)];
+        l3_pgentry_t *pl3e;
+        l2_pgentry_t *pl2e;
+        l1_pgentry_t *l1t;
+
+        if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) )
+        {
+            pl3e = alloc_xen_pagetable();
+            BUG_ON(!pl3e);
+            clear_page(pl3e);
+            efi_l4_pgtable[l4_table_offset(addr)] =
+                l4e_from_paddr(virt_to_maddr(pl3e), __PAGE_HYPERVISOR);
+        }
+        else
+            pl3e = l4e_to_l3e(l4e);
+        pl3e += l3_table_offset(addr);
+        if ( !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) )
+        {
+            pl2e = alloc_xen_pagetable();
+            BUG_ON(!pl2e);
+            clear_page(pl2e);
+            *pl3e = l3e_from_paddr(virt_to_maddr(pl2e), __PAGE_HYPERVISOR);
+        }
+        else
+        {
+            BUG_ON(l3e_get_flags(*pl3e) & _PAGE_PSE);
+            pl2e = l3e_to_l2e(*pl3e);
+        }
+        pl2e += l2_table_offset(addr);
+        if ( !(l2e_get_flags(*pl2e) & _PAGE_PRESENT) )
+        {
+            l1t = alloc_xen_pagetable();
+            BUG_ON(!l1t);
+            clear_page(l1t);
+            *pl2e = l2e_from_paddr(virt_to_maddr(l1t), __PAGE_HYPERVISOR);
+        }
+        else
+        {
+            BUG_ON(l2e_get_flags(*pl2e) & _PAGE_PSE);
+            l1t = l2e_to_l1e(*pl2e);
+        }
+        for ( i = l1_table_offset(addr);
+              i < L1_PAGETABLE_ENTRIES && extra->smfn < extra->emfn;
+              ++i, ++extra->smfn )
+            l1t[i] = l1e_from_pfn(extra->smfn, extra->prot);
+
+        if ( extra->smfn == extra->emfn )
+        {
+            extra_head = extra->next;
+            xfree(extra);
+        }
+    }
+
+    /* Insert Xen mappings. */
+    for ( i = l4_table_offset(HYPERVISOR_VIRT_START);
+          i < l4_table_offset(DIRECTMAP_VIRT_END); ++i )
+        efi_l4_pgtable[i] = idle_pg_table[i];
+#endif
+}
+
+static void __init place_string(u32 *addr, const char *s)
+{
+    static char *__initdata alloc = start;
+
+    if ( s && *s )
+    {
+        size_t len1 = strlen(s) + 1;
+        const char *old = (char *)(long)*addr;
+        size_t len2 = *addr ? strlen(old) + 1 : 0;
+
+        alloc -= len1 + len2;
+        /*
+         * Insert new string before already existing one. This is needed
+         * for options passed on the command line to override options from
+         * the configuration file.
+         */
+        memcpy(alloc, s, len1);
+        if ( *addr )
+        {
+            alloc[len1 - 1] = ' ';
+            memcpy(alloc + len1, old, len2);
+        }
+    }
+    *addr = (long)alloc;
+}
+
