armv8: Refactor EFI loader
authorDaniel Schwyn <daniel.schwyn@inf.ethz.ch>
Fri, 14 Jun 2019 15:26:37 +0000 (17:26 +0200)
committerDaniel Schwyn <daniel.schwyn@inf.ethz.ch>
Fri, 27 Sep 2019 07:55:33 +0000 (09:55 +0200)
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>

tools/armv8_bootimage/armv8_bootimage.c
tools/armv8_bootimage/blob.h
tools/armv8_bootimage/efi_loader.c

index 3df8266..847264d 100644 (file)
@@ -282,9 +282,9 @@ void *create_multiboot_info(struct menu_lst *menu,
     }
     /* Add the EFI MMAP tag */
     {
-        struct multiboot_tag_efi_mmap *tag =
+        struct multiboot_tag_efi_mmap *mmap_tag =
             (struct multiboot_tag_efi_mmap *) cursor;
-        tag->type = MULTIBOOT_TAG_TYPE_EFI_MMAP;
+        mmap_tag->type = MULTIBOOT_TAG_TYPE_EFI_MMAP;
         cursor += sizeof(struct multiboot_tag_efi_mmap);
     }
     return mb;
@@ -555,6 +555,10 @@ int main(int argc, char *argv[])
 
     memset(blob.data, 0, sizeof(blob.data));
 
+    for (size_t i = 0; i < menu->nmodules + 2; i++) {
+        blob.modules_size += modules[i].len;
+    }
+
     pa = phys_alloc(bd_image[0].no_relocations *
                     sizeof(struct Blob_relocation), BASE_PAGE_SIZE);
     printf("Boot relocations PA %016zx,%d\n", pa,
@@ -562,13 +566,16 @@ int main(int argc, char *argv[])
     blob.boot_driver_relocations = pa;
     blob.boot_driver_relocations_count = bd_image[0].no_relocations;
     blob.boot_driver_segment = bd_image[0].segment.base;
+    blob.boot_driver_segment_size = bd_image[0].segment.npages * BASE_PAGE_SIZE;
+
     pa = phys_alloc(bd_image[1].no_relocations *
                     sizeof(struct Blob_relocation), BASE_PAGE_SIZE);
     printf("Kernel relocations PA %016zx,%d\n", pa,
            bd_image[1].no_relocations);
-    blob.kernel_relocations = pa;
-    blob.kernel_relocations_count = bd_image[1].no_relocations;
-    blob.kernel_segment = bd_image[1].segment.base;
+    blob.cpu_driver_relocations = pa;
+    blob.cpu_driver_relocations_count = bd_image[1].no_relocations;
+    blob.cpu_driver_segment = bd_image[1].segment.base;
+    blob.cpu_driver_segment_size = bd_image[1].segment.npages * BASE_PAGE_SIZE;
 
     /*** Create the multiboot info header. ***/
     size_t mb_size, size;
@@ -582,8 +589,9 @@ int main(int argc, char *argv[])
 
     blob.magic = 0x12345678fedcba90;
     blob.multiboot = mb_base;
+    blob.multiboot_size = mb_size;
     blob.boot_driver_entry = (paddr_t) bd_image[0].entry;
-    blob.kernel_entry = (paddr_t) bd_image[1].entry;
+    blob.cpu_driver_entry = (paddr_t) bd_image[1].entry;
 
     size_t r;
     int fd = open(outfile, O_CREAT | O_WRONLY,
index 7da93d2..dc9a544 100644 (file)
@@ -20,14 +20,19 @@ struct Blob {                   // offsets
         struct {
             uint64_t magic;
             uint64_t multiboot; // offset of the Multiboot2 boot info
+            uint64_t multiboot_size;
+            uint64_t modules;
+            uint64_t modules_size;
             uint64_t boot_driver_entry;
             uint64_t boot_driver_segment;       // offset of the boot driver image
+            uint64_t boot_driver_segment_size;
             uint64_t boot_driver_relocations;
             uint64_t boot_driver_relocations_count;
-            uint64_t kernel_entry;
-            uint64_t kernel_segment;    // offset of the cpu kernel image
-            uint64_t kernel_relocations;
-            uint64_t kernel_relocations_count;
+            uint64_t cpu_driver_entry;
+            uint64_t cpu_driver_segment;    // offset of the cpu kernel image
+            uint64_t cpu_driver_segment_size;
+            uint64_t cpu_driver_relocations;
+            uint64_t cpu_driver_relocations_count;
         };
         unsigned char data[BASE_PAGE_SIZE];
     };
index 1764d60..b82cb18 100644 (file)
@@ -33,113 +33,183 @@ typedef enum {
     EfiBarrelfishMaxMemType
 } EFI_BARRELFISH_MEMORY_TYPE;
 
+static const char *mmap_types[] = {
+    "reserved",
+    "LD code",
+    "LD data",
+    "BS code",
+    "BS data",
+    "RS code",
+    "RS data",
+    "available",
+    "unusable",
+    "ACPI reclaim",
+    "ACPI NVS",
+    "MMIO",
+    "ports",
+    "PAL code",
+    "persist"
+};
+
+static const char *bf_mmap_types[] = {
+    "BF code",
+    "BF stack",
+    "BF multiboot",
+    "BF module",
+    "BF page table",
+    "BF core data",
+};
+
+struct config {
+    struct multiboot_info *multiboot;
+    struct multiboot_tag_efi_mmap *mmap_tag;
+    struct multiboot_tag_string *cmd_tag;
+    EFI_PHYSICAL_ADDRESS modules;
+    EFI_VIRTUAL_ADDRESS boot_driver_entry;
+    EFI_PHYSICAL_ADDRESS cpu_driver_entry;
+    EFI_PHYSICAL_ADDRESS cpu_driver_stack;
+    size_t cpu_driver_stack_size;
+};
+
 void *memcpy(void *dest, const void *src, __SIZE_TYPE__ n);
+void *memset(void *dest, int value, __SIZE_TYPE__ n);
+
+typedef void boot_driver(uint32_t magic, void *pointer);
 
-extern uint64_t barrelfish_blob_start[1];
-extern uint64_t barrelfish_blob_end[1];
+extern char barrelfish_blob_start[1];
 
-#define HIGH_MEMORY 0xffff000000000000
+#define KERNEL_OFFSET       0xffff000000000000
+#define KERNEL_STACK_SIZE   0x4000
 
 #define ROUND_UP(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+#define BLOB_ADDRESS(offset) (barrelfish_blob_start + (offset))
+
+/* Copy a base+length string into a null-terminated string.  Destination
+ * buffer must be large enough to hold the terminator i.e. n+1 characters. */
+static inline void
+ntstring(char *dest, const char *src, size_t len) {
+    memcpy(dest, src, len);
+    dest[len]= '\0';
+}
 
 #define MEM_MAP_SIZE 8192
 char mmap[MEM_MAP_SIZE];
 UINTN mmap_size, mmap_key, mmap_d_size;
 UINT32 mmap_d_ver;
 
-void get_memory_map(void)
+static EFI_STATUS
+update_memory_map(void)
 {
     EFI_STATUS status;
-    unsigned mmap_n_desc, i;
+    size_t mmap_n_desc, i;
 
     /* Grab the current table from UEFI. */
     mmap_size = MEM_MAP_SIZE;
-    status = ST->BootServices->GetMemoryMap(&mmap_size, (void *) &mmap,
-                                            &mmap_key, &mmap_d_size,
-                                            &mmap_d_ver);
+    status = ST->BootServices->GetMemoryMap(
+        &mmap_size,
+        (void *) &mmap,
+        &mmap_key,
+        &mmap_d_size,
+        &mmap_d_ver
+    );
     if (status == EFI_BUFFER_TOO_SMALL) {
         Print(L"The memory map is %dB, but MEM_MAP_SIZE is %d.\n",
-              mmap_size, MEM_MAP_SIZE);
+            mmap_size, MEM_MAP_SIZE);
         Print(L"This is compile-time limit in Hagfish - please report "
               L"this overflow, it's a bug.\n");
-        return;
+        return status;
     } else if (EFI_ERROR(status)) {
-        Print(L"GetMemoryMap: %r\n", status);
-        return;
+        Print(L"Unable to get memory map: %x\n", status);
+        return status;
     }
 
-    Print(L"Memory map at %lx, key: %x, descriptor version: %x\n",
-          mmap, mmap_key, mmap_d_ver);
     mmap_n_desc = mmap_size / mmap_d_size;
-    Print(L"Got %d memory map entries of %dB (%dB).\n",
-          mmap_n_desc, mmap_d_size, mmap_size);
 
-    Print
-        (L"Type          VStart           PStart           PEnd      Attributes\n");
-    for (i = 0; i < mmap_n_desc; i++) {
-        EFI_MEMORY_DESCRIPTOR *desc = ((void *) mmap) + (mmap_d_size * i);
-        desc->VirtualStart = desc->PhysicalStart + HIGH_MEMORY;
-        Print(L"%3d %016lx %016lx %016lx %016lx\n",
-              desc->Type, desc->VirtualStart,
-              desc->PhysicalStart,
-              desc->PhysicalStart + (desc->NumberOfPages << 12),
-              desc->Attribute);
+    return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+relocate_memory_map(void) {
+    if (!mmap_size) {
+        return EFI_LOAD_ERROR;
     }
+
+    size_t mmap_n_desc = mmap_size / mmap_d_size;
+
+    for (size_t i= 0; i < mmap_n_desc; i++) {
+        EFI_MEMORY_DESCRIPTOR *desc =
+            (EFI_MEMORY_DESCRIPTOR *)(mmap + i * mmap_d_size);
+        // 1:1 mapping into kernel window
+        desc->VirtualStart = desc->PhysicalStart + KERNEL_OFFSET;
+    }
+
+    return EFI_SUCCESS;
 }
 
-void relocateMultiboot(void *base, uint64_t offset,
-                       uint64_t kernel_segment)
+static void
+print_memory_map(int update)
 {
-    struct multiboot_info *multiboot = (struct multiboot_info *)(base + offset);
+    if (update) {
+        update_memory_map();
+    }
 
-    get_memory_map();
+    size_t mmap_n_desc = mmap_size / mmap_d_size;
 
-    struct multiboot_tag *tag;
-    Print(L"Relocating multiboot: %lx\n", multiboot);
-    for (tag = multiboot->tags; tag->type != MULTIBOOT_TAG_TYPE_END; tag = (void *)tag + tag->size) {
-        Print(L"%lx: tag %d:%d\n", tag, tag->type, tag->size);
-        if (tag->type == MULTIBOOT_TAG_TYPE_MODULE_64) {
-            struct multiboot_tag_module_64 *mtag = (struct multiboot_tag_module_64 *) tag;
-            Print(L"\tbefore %lx:%lx\n", mtag->mod_start, mtag->mod_end);
-            mtag->mod_start += (uint64_t) base;
-            mtag->mod_end += (uint64_t) base;
-            Print(L"\tafter  %lx:%lx\n", mtag->mod_start, mtag->mod_end);
-        } else if (tag->type == MULTIBOOT_TAG_TYPE_EFI_MMAP) {
-            struct multiboot_tag_efi_mmap *emtag = (struct multiboot_tag_efi_mmap *)tag;
-            emtag->descr_size = mmap_d_size;
-            emtag->descr_vers = mmap_d_ver;
-            emtag->size = sizeof(struct multiboot_tag_efi_mmap) + mmap_size;
-            memcpy((void *) emtag + sizeof(struct multiboot_tag_efi_mmap),
-                   (uint64_t *) mmap, mmap_size);
-
-            /* Add the end tag now that we know how large the memory map is */
-            struct multiboot_tag *end_tag = (void *)emtag + emtag->size;
-            end_tag->type = MULTIBOOT_TAG_TYPE_END;
-            end_tag->size = ROUND_UP(sizeof(struct multiboot_tag), MULTIBOOT_TAG_ALIGN);
-            multiboot->total_size = (void *)end_tag + end_tag->size - (void *)multiboot;
-            Print(L"Size: %x\n", multiboot->total_size);
+    Print(L"Memory map at %lx, key: %x, descriptor version: %x\n",
+            mmap, mmap_key, mmap_d_ver);
+    Print(L"Got %d memory map entries of %dB (%dB).\n",
+               mmap_n_desc, mmap_d_size, mmap_size);
+
+    Print(L"Type          PStart           PEnd        "
+               "      Size       Attributes\n");
+    for (UINTN i = 0; i < mmap_n_desc; i++) {
+        EFI_MEMORY_DESCRIPTOR *desc = ((void *) mmap) + (mmap_d_size * i);
+
+        const char *description;
+        if (desc->Type < EfiMaxMemoryType) {
+            description= mmap_types[desc->Type];
+        }
+        else if (
+            EfiBarrelfishFirstMemType <= desc->Type &&
+            desc->Type < EfiBarrelfishMaxMemType
+        ) {
+            description = bf_mmap_types[desc->Type - EfiBarrelfishFirstMemType];
         }
+        else {
+            description= "???";
+        }
+        
+        Print(L"%-13a %016lx %016lx %9ldkB %01x\n",
+              description,
+              desc->PhysicalStart,
+              desc->PhysicalStart + (desc->NumberOfPages << 12) - 1,
+              (desc->NumberOfPages << 12) / 1024,
+              desc->Attribute);
     }
 }
 
-void relocateElf(void *base, uint64_t offset, uint64_t virtual_offset,
-                 struct Blob_relocation *relocations,
-                 unsigned no_relocations)
-{
-    unsigned i;
-
-    Print(L"Relocating ELF %lx %lx %lx %d\n", base, offset, relocations,
-          no_relocations);
-    for (i = 0; i < no_relocations; i++) {
-        *(uint64_t *) (base + offset + relocations[i].offset) =
-            relocations[i].addend + (uint64_t) base + offset +
-            virtual_offset;
+static lpaddr_t get_root_table(void) {
+    Print(L"Getting page table address\n");
+    uint64_t el, ttbr0;
+  __asm("mrs %0, currentel\n":"=r"(el));
+    el >>= 2;
+    Print(L"Currently in EL%lx\n", el);
+
+    if (el == 1) {
+      __asm("mrs %0, ttbr0_el1\n":"=r"(ttbr0));
+    } else if (el == 2) {                    // 2
+      __asm("mrs %0, ttbr0_el2\n":"=r"(ttbr0));
+    } else {
+        Print(L"Can't get page table\n");
+        return 0;
     }
+    Print(L"Page table at 0x%lx\n", ttbr0);
+    return ttbr0;
 }
 
-typedef void boot_driver(uint32_t magic, void *pointer);
-
-void dump_pages(void)
+static void dump_pages(void)
 {
     uint64_t i, j, k, l;
 
@@ -200,145 +270,308 @@ void dump_pages(void)
     }
 }
 
-static lpaddr_t get_root_table(void) {
-    Print(L"Getting page table address\n");
-    uint64_t el, ttbr0;
-  __asm("mrs %0, currentel\n":"=r"(el));
-    el >>= 2;
-    Print(L"Currently in EL%lx\n", el);
+static void
+relocate_elf(EFI_PHYSICAL_ADDRESS segment_start, uint64_t virtual_offset,
+                 struct Blob_relocation *relocations,
+                 uint64_t no_relocations)
+{
+    Print(L"Relocating ELF %lx %lx %d\n",
+        segment_start, relocations, no_relocations);
+    for (uint64_t i = 0; i < no_relocations; i++) {
+        *(uint64_t *)(segment_start + relocations[i].offset) =
+            segment_start + virtual_offset + relocations[i].addend;
+    }
+}
 
-    if (el == 1) {
-      __asm("mrs %0, ttbr0_el1\n":"=r"(ttbr0));
-    } else if (el == 2) {                    // 2
-      __asm("mrs %0, ttbr0_el2\n":"=r"(ttbr0));
-    } else {
-        Print(L"Can't get page table\n");
-        return 0;
+static EFI_STATUS
+relocate_boot_driver(struct Blob *blob_info, struct config *cfg)
+{
+    EFI_STATUS status;
+
+    /* Should be page aligend */
+    ASSERT(blob_info->boot_driver_segment_size % BASE_PAGE_SIZE == 0);
+
+    EFI_PHYSICAL_ADDRESS boot_driver;
+    status = BS->AllocatePages(
+        AllocateAnyPages,
+        EfiBarrelfishCPUDriver,
+        blob_info->boot_driver_segment_size / BASE_PAGE_SIZE,
+        &boot_driver
+    );
+    if (EFI_ERROR(status)) {
+        Print(L"Error allocating memory for boot driver segment: %d\n", status);
+        return status;
     }
-    Print(L"Page table at 0x%lx\n", ttbr0);
-    return ttbr0;
+
+    memcpy((void *)boot_driver, BLOB_ADDRESS(blob_info->boot_driver_segment), blob_info->boot_driver_segment_size);
+
+    struct Blob_relocation *boot_driver_relocations =
+        (struct Blob_relocation *)BLOB_ADDRESS(blob_info->boot_driver_relocations);
+    relocate_elf(
+        boot_driver,
+        0,
+        boot_driver_relocations,
+        blob_info->boot_driver_relocations_count
+    );
+
+    cfg->boot_driver_entry = boot_driver + blob_info->boot_driver_entry;
+    
+    return EFI_SUCCESS;
 }
 
-EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle,
-                           EFI_SYSTEM_TABLE * SystemTable)
+static EFI_STATUS
+relocate_cpu_driver(struct Blob *blob_info, struct config *cfg)
 {
-    uint64_t i, j, k, l;
     EFI_STATUS status;
 
-    InitializeLib(ImageHandle, SystemTable);
+    /* Should be page aligend */
+    ASSERT(blob_info->cpu_driver_segment_size % BASE_PAGE_SIZE == 0);
+
+    EFI_PHYSICAL_ADDRESS cpu_driver;
+    status = BS->AllocatePages(
+        AllocateAnyPages,
+        EfiBarrelfishCPUDriver,
+        blob_info->cpu_driver_segment_size / BASE_PAGE_SIZE,
+        &cpu_driver
+    );
+    if (EFI_ERROR(status)) {
+        Print(L"Error allocating memory for CPU driver segment: %d\n", status);
+        return status;
+    }
+
+    memcpy((void *)cpu_driver, BLOB_ADDRESS(blob_info->cpu_driver_segment), blob_info->cpu_driver_segment_size);
 
-    Print(L"Blob: %lx %lx\n", barrelfish_blob_start, barrelfish_blob_end);
+    struct Blob_relocation *cpu_driver_relocations =
+        (struct Blob_relocation *)BLOB_ADDRESS(blob_info->cpu_driver_relocations);
+    relocate_elf(
+        cpu_driver,
+        KERNEL_OFFSET,
+        cpu_driver_relocations,
+        blob_info->cpu_driver_relocations_count
+    );
 
-    uint64_t blob_size =
-        (void *) barrelfish_blob_end - (void *) barrelfish_blob_start;
-    uint64_t blob_no_pages = (blob_size + 4095) / 4096;
+    cfg->cpu_driver_entry = cpu_driver + blob_info->cpu_driver_entry + KERNEL_OFFSET;
 
-    void *relocated_blob;
-    status =
-        BS->AllocatePages(AllocateAnyPages, EfiLoaderCode, blob_no_pages,
-                          (uint64_t *) & relocated_blob);
+    status = BS->AllocatePages(
+        AllocateAnyPages,
+        EfiBarrelfishCPUDriverStack,
+        KERNEL_STACK_SIZE / BASE_PAGE_SIZE,
+        &cfg->cpu_driver_stack
+    );
     if (EFI_ERROR(status)) {
-        Print(L"AllocatePages: ERROR %d, %x\n", status, mmap_key);
+        Print(L"Error allocating memory for CPU driver stack: %d\n", status);
         return status;
     }
-    Print(L"Blob  size:%ld  pages:%ld -> %lx\n", blob_size,
-          blob_no_pages, relocated_blob);
-    // move the blob to the unprotected region so we can execute it
-    memcpy(relocated_blob, barrelfish_blob_start, blob_size);
-
-    struct Blob *blob = (struct Blob *) relocated_blob;
-    Print(L"Magic: %lx\n", blob->magic);
-    Print(L"Multiboot: %lx\n", blob->multiboot);
-    Print(L"Boot driver entry: %lx\n", blob->boot_driver_entry);
-    Print(L"Boot driver segment: %lx\n", blob->boot_driver_segment);
-    Print(L"Boot driver relocations: %lx\n",
-          blob->boot_driver_relocations);
-    Print(L"Boot driver relocations count: %lx\n",
-          blob->boot_driver_relocations_count);
-    Print(L"Kernel entry: %lx\n", blob->kernel_entry);
-    Print(L"Kernel segment: %lx\n", blob->kernel_segment);
-    Print(L"Kernel relocations: %lx\n", blob->kernel_relocations);
-    Print(L"Kernel relocations count: %lx\n",
-          blob->kernel_relocations_count);
-
-    relocateMultiboot((void *) blob, blob->multiboot,
-                      blob->kernel_segment);
-    relocateElf((void *) blob, blob->boot_driver_segment, 0,
-                (struct Blob_relocation *) ((void *) blob +
-                                            blob->boot_driver_relocations),
-                blob->boot_driver_relocations_count);
-    relocateElf((void *) blob, blob->kernel_segment, HIGH_MEMORY,
-                (struct Blob_relocation *) ((void *) blob +
-                                            blob->kernel_relocations),
-                blob->kernel_relocations_count);
-
-    uint32_t *pc;
-    pc = (void *) blob + blob->boot_driver_segment +
-        blob->boot_driver_entry;
-
-    void *multiboot;
-    multiboot = (void *) blob + blob->multiboot;
-
-    void *stack;
-    status = BS->AllocatePages(AllocateAnyPages, EfiBarrelfishCPUDriverStack, 4, &stack);
+
+    cfg->cpu_driver_stack_size = KERNEL_STACK_SIZE;
+
+    Print(
+        L"Relocated CPU driver entry point is %lx, stack at %lx\n",
+        cfg->cpu_driver_entry,
+        cfg->cpu_driver_stack
+    );
+    
+    return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+relocate_modules(struct Blob *blob_info, struct config *cfg)
+{
+    EFI_STATUS status;
+    
+    /* Should be page aligend */
+    ASSERT(blob_info->modules_size % BASE_PAGE_SIZE == 0);
+
+    status = BS->AllocatePages(
+        AllocateAnyPages,
+        EfiBarrelfishELFData,
+        blob_info->modules_size / BASE_PAGE_SIZE,
+        &cfg->modules
+    );
     if (EFI_ERROR(status)) {
-        Print(L"AllocatePages: ERROR %d, %x\n", status, mmap_key);
+        Print(L"Error allocating memory for modules: %d\n", status);
         return status;
     }
 
-    struct armv8_core_data *core_data;
-    status = BS->AllocatePages(AllocateAnyPages, EfiBarrelfishCoreData, 1, &core_data);
+    memcpy((void *)cfg->modules, BLOB_ADDRESS(blob_info->modules), blob_info->modules_size);
+    return EFI_SUCCESS;
+}
+
+static EFI_STATUS
+relocate_multiboot(struct Blob *blob_info, struct config *cfg)
+{
+    EFI_STATUS status;
+
+    /* Should be page aligend */
+    ASSERT(blob_info->multiboot_size % BASE_PAGE_SIZE == 0);
+
+    EFI_PHYSICAL_ADDRESS memory;
+    status = BS->AllocatePages(
+        AllocateAnyPages,
+        EfiBarrelfishMultibootData,
+        blob_info->multiboot_size / BASE_PAGE_SIZE,
+        (EFI_PHYSICAL_ADDRESS *)&cfg->multiboot
+    );
     if (EFI_ERROR(status)) {
-        Print(L"AllocatePages: ERROR %d, %x\n", status, mmap_key);
+        Print(L"Error allocating memory for multiboot info: %d\n", status);
         return status;
     }
 
-    core_data->boot_magic = ARMV8_BOOTMAGIC_BSP;
-    core_data->cpu_driver_stack = (lpaddr_t)(stack + (4 * 4096) - 16);
-    core_data->cpu_driver_stack_limit = (lpaddr_t)stack;
-    core_data->cpu_driver_entry = (lvaddr_t)((void *) blob + blob->kernel_segment +
-        blob->kernel_entry + HIGH_MEMORY);
-    core_data->page_table_root = get_root_table();
+    memcpy(cfg->multiboot, BLOB_ADDRESS(blob_info->multiboot), blob_info->multiboot_size);
+
+    /* Module start & end pointed into the blob.
+     * Now they need to point into the module region
+     */
+    uint64_t module_offset = (uint64_t)cfg->modules - blob_info->modules;
+
+    Print(L"Relocating multiboot info: %lx\n", cfg->multiboot);
+    /* We don't have an end tag yet, but EFI mmap tag is last */
+    struct multiboot_tag *tag;
+    for (tag = cfg->multiboot->tags; tag->type != MULTIBOOT_TAG_TYPE_EFI_MMAP; tag = (void *)tag + tag->size) {
+        Print(L"%lx: tag %d:%d\n", tag, tag->type, tag->size);
+
+        if (tag->type == MULTIBOOT_TAG_TYPE_MODULE_64) {
+            struct multiboot_tag_module_64 *mtag = (struct multiboot_tag_module_64 *)tag;
+            Print(L"\tbefore %lx:%lx\n", mtag->mod_start, mtag->mod_end);
+            mtag->mod_start += module_offset;
+            mtag->mod_end += module_offset;
+            Print(L"\tafter  %lx:%lx\n", mtag->mod_start, mtag->mod_end);
+        }
+        else if (tag->type == MULTIBOOT_TAG_TYPE_CMDLINE) {
+            cfg->cmd_tag = (struct multiboot_tag_string *)tag;
+        }
+    }
+
+    ASSERT(tag->type == MULTIBOOT_TAG_TYPE_EFI_MMAP);
+    cfg->mmap_tag = (struct multiboot_tag_efi_mmap *)tag;
 
-    struct multiboot_tag_string *cmd_tag = ((struct multiboot_info *)multiboot)->tags;
-    while (cmd_tag->type != MULTIBOOT_TAG_TYPE_CMDLINE) {
-        cmd_tag = (void *)cmd_tag + cmd_tag->size;
+    return EFI_SUCCESS;
+}
+
+static struct armv8_core_data *
+create_core_data(struct config *cfg)
+{
+    EFI_STATUS status;
+
+    struct armv8_core_data *core_data;
+    status = BS->AllocatePages(
+        AllocateAnyPages,
+        EfiBarrelfishCoreData,
+        1,
+        (EFI_PHYSICAL_ADDRESS *)&core_data
+    );
+    if (EFI_ERROR(status)) {
+        Print(L"Error allocating memory for core data: %d\n", status);
+        return NULL;
     }
-    memcpy(
+
+    memset(core_data, 0, BASE_PAGE_SIZE);
+
+    core_data->boot_magic = ARMV8_BOOTMAGIC_BSP;
+    core_data->cpu_driver_stack =
+        (lpaddr_t)cfg->cpu_driver_stack + cfg->cpu_driver_stack_size - 16;
+    core_data->cpu_driver_stack_limit = (lpaddr_t)cfg->cpu_driver_stack;
+    core_data->cpu_driver_entry = (lvaddr_t)cfg->cpu_driver_entry;
+    core_data->page_table_root = get_root_table();
+    ntstring(
         core_data->cpu_driver_cmdline,
-        cmd_tag->string,
-        cmd_tag->size - sizeof(cmd_tag->type)
+        cfg->cmd_tag->string,
+        MIN(
+            cfg->cmd_tag->size - sizeof(struct multiboot_tag_string),
+            sizeof(core_data->cpu_driver_cmdline) - 1
+        )
     );
-    core_data->cpu_driver_cmdline[cmd_tag->size - sizeof(cmd_tag->type)] = 0;
     
-    core_data->multiboot_image.base = (lpaddr_t)multiboot;
-    core_data->multiboot_image.length = ((struct multiboot_info *)multiboot)->total_size;
+    core_data->multiboot_image.base = (lpaddr_t)cfg->multiboot;
+    core_data->multiboot_image.length = cfg->multiboot->total_size;
+    core_data->efi_mmap = (lpaddr_t)cfg->mmap_tag;
+
+    return core_data;
+}
+
+EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle,
+                           EFI_SYSTEM_TABLE * SystemTable)
+{
+    EFI_STATUS status;
+
+    InitializeLib(ImageHandle, SystemTable);
+    
+    struct Blob *blob_info = (struct Blob *)BLOB_ADDRESS(0);
+    Print(L"Blob is at: 0x%lx\n", blob_info);
+    Print(L"Magic: %lx\n", blob_info->magic);
 
-    struct multiboot_tag_string *mmap_tag = ((struct multiboot_info *)multiboot)->tags;
-    while (mmap_tag->type != MULTIBOOT_TAG_TYPE_EFI_MMAP) {
-        mmap_tag = (void *)mmap_tag + mmap_tag->size;
+    struct config cfg;
+
+    status = relocate_boot_driver(blob_info, &cfg);
+    if (EFI_ERROR(status)) {
+        Print(L"Failed to relocate boot driver\n");
+        return status;
+    }
+
+    status = relocate_cpu_driver(blob_info, &cfg);
+    if (EFI_ERROR(status)) {
+        Print(L"Failed to relocate CPU driver\n");
+        return status;
+    }
+
+    status = relocate_modules(blob_info, &cfg);
+    if (EFI_ERROR(status)) {
+        Print(L"Failed to relocate modules\n");
+        return status;
     }
-    core_data->efi_mmap = (lpaddr_t)mmap_tag;
 
-    Print(L"args(%lx, %lx)\n", MULTIBOOT2_BOOTLOADER_MAGIC, core_data);
+    status = relocate_multiboot(blob_info, &cfg);
+    if (EFI_ERROR(status)) {
+        Print(L"Failed to relocate multiboot info\n");
+        return status;
+    }
+
+    struct armv8_core_data *core_data = create_core_data(&cfg);
+
+    Print(L"Terminating boot services and jumping to image at 0x%lx\n", cfg.boot_driver_entry);
+    Print(L"Core data pointer is %lx\n", core_data);
+
+    print_memory_map(1);
+
+    update_memory_map();
 
     status = ST->BootServices->ExitBootServices(ImageHandle, mmap_key);
     if (EFI_ERROR(status)) {
-        Print(L"ExitBootServices: ERROR %d, %x\n", status, mmap_key);
+        Print(L"Error exiting boot services: %d, %x\n", status, mmap_key);
         return status;
     }
 
-    status =
-        ST->RuntimeServices->SetVirtualAddressMap(mmap_size, mmap_d_size,
-                                                  mmap_d_ver,
-                                                  (void *) &mmap);
+    /*** EFI boot services are now terminated, we're on our own. */
+    status = relocate_memory_map();
+    if (EFI_ERROR(status)) {
+        return EFI_SUCCESS;
+    }
+
+    /* The last thing we do is complete the multiboot info:
+     * Set the EFI mmap and end tag
+     */
+    cfg.mmap_tag->size = ROUND_UP(sizeof(struct multiboot_tag_efi_mmap) + mmap_size, 8);
+    cfg.mmap_tag->descr_size = mmap_d_size;
+    cfg.mmap_tag->descr_vers = mmap_d_ver;
+    memcpy(cfg.mmap_tag->efi_mmap, mmap, mmap_size);
+
+    struct multiboot_tag *end_tag = (void *)cfg.mmap_tag + cfg.mmap_tag->size;
+    end_tag->type = MULTIBOOT_TAG_TYPE_END;
+    end_tag->size = ROUND_UP(sizeof(struct multiboot_tag), 8);
+    cfg.multiboot->total_size = (void *)end_tag + end_tag->size - (void *)cfg.multiboot;
+
+    status = ST->RuntimeServices->SetVirtualAddressMap(
+        mmap_size,
+        mmap_d_size,
+        mmap_d_ver,
+        (void *) &mmap
+    );
     if (EFI_ERROR(status)) {
-        Print(L"SetVirtualAddressMap: ERROR %d\n", status);
         return status;
     }
 
     // Jump to the bootloader, the blob can be reused
-    (*((boot_driver *) (pc))) (MULTIBOOT2_BOOTLOADER_MAGIC, core_data);
+    (*((boot_driver *) (cfg.boot_driver_entry))) (MULTIBOOT2_BOOTLOADER_MAGIC, core_data);
 
     return EFI_SUCCESS;
 }