armv8: Refactor EFI loader
[barrelfish] / tools / armv8_bootimage / efi_loader.c
1 /*
2  * An EFI loader for Barrelfish
3  *
4  * This is an EFI app which loads the Multiboot2 image and execute
5  * the bootloader.
6  * This object file is linked together with the Multiboot2 image into one object
7  *
8  * Copyright (c) 2018, ETH Zurich.
9  * All rights reserved.
10  *
11  * This file is distributed under the terms in the attached LICENSE file.
12  * If you do not find this file, copies can be found by writing to:
13  * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
14  */
15
16 #include <efi/efi.h>
17 #include <efi/efilib.h>
18 #include <multiboot2.h>
19 #include "../../include/barrelfish_kpi/types.h"
20 #include "../../include/target/aarch64/barrelfish_kpi/arm_core_data.h"
21 #include "blob.h"
22
23 typedef enum {
24     EfiBarrelfishFirstMemType=      0x80000000,
25
26     EfiBarrelfishCPUDriver=         0x80000000,
27     EfiBarrelfishCPUDriverStack=    0x80000001,
28     EfiBarrelfishMultibootData=     0x80000002,
29     EfiBarrelfishELFData=           0x80000003,
30     EfiBarrelfishBootPageTable=     0x80000004,
31     EfiBarrelfishCoreData=          0x80000005,
32
33     EfiBarrelfishMaxMemType
34 } EFI_BARRELFISH_MEMORY_TYPE;
35
36 static const char *mmap_types[] = {
37     "reserved",
38     "LD code",
39     "LD data",
40     "BS code",
41     "BS data",
42     "RS code",
43     "RS data",
44     "available",
45     "unusable",
46     "ACPI reclaim",
47     "ACPI NVS",
48     "MMIO",
49     "ports",
50     "PAL code",
51     "persist"
52 };
53
54 static const char *bf_mmap_types[] = {
55     "BF code",
56     "BF stack",
57     "BF multiboot",
58     "BF module",
59     "BF page table",
60     "BF core data",
61 };
62
63 struct config {
64     struct multiboot_info *multiboot;
65     struct multiboot_tag_efi_mmap *mmap_tag;
66     struct multiboot_tag_string *cmd_tag;
67     EFI_PHYSICAL_ADDRESS modules;
68     EFI_VIRTUAL_ADDRESS boot_driver_entry;
69     EFI_PHYSICAL_ADDRESS cpu_driver_entry;
70     EFI_PHYSICAL_ADDRESS cpu_driver_stack;
71     size_t cpu_driver_stack_size;
72 };
73
74 void *memcpy(void *dest, const void *src, __SIZE_TYPE__ n);
75 void *memset(void *dest, int value, __SIZE_TYPE__ n);
76
77 typedef void boot_driver(uint32_t magic, void *pointer);
78
79 extern char barrelfish_blob_start[1];
80
81 #define KERNEL_OFFSET       0xffff000000000000
82 #define KERNEL_STACK_SIZE   0x4000
83
84 #define ROUND_UP(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
85 #define MIN(x, y) ((x) < (y) ? (x) : (y))
86
87 #define BLOB_ADDRESS(offset) (barrelfish_blob_start + (offset))
88
89 /* Copy a base+length string into a null-terminated string.  Destination
90  * buffer must be large enough to hold the terminator i.e. n+1 characters. */
91 static inline void
92 ntstring(char *dest, const char *src, size_t len) {
93     memcpy(dest, src, len);
94     dest[len]= '\0';
95 }
96
97 #define MEM_MAP_SIZE 8192
98 char mmap[MEM_MAP_SIZE];
99 UINTN mmap_size, mmap_key, mmap_d_size;
100 UINT32 mmap_d_ver;
101
102 static EFI_STATUS
103 update_memory_map(void)
104 {
105     EFI_STATUS status;
106     size_t mmap_n_desc, i;
107
108     /* Grab the current table from UEFI. */
109     mmap_size = MEM_MAP_SIZE;
110     status = ST->BootServices->GetMemoryMap(
111         &mmap_size,
112         (void *) &mmap,
113         &mmap_key,
114         &mmap_d_size,
115         &mmap_d_ver
116     );
117     if (status == EFI_BUFFER_TOO_SMALL) {
118         Print(L"The memory map is %dB, but MEM_MAP_SIZE is %d.\n",
119             mmap_size, MEM_MAP_SIZE);
120         Print(L"This is compile-time limit in Hagfish - please report "
121               L"this overflow, it's a bug.\n");
122         return status;
123     } else if (EFI_ERROR(status)) {
124         Print(L"Unable to get memory map: %x\n", status);
125         return status;
126     }
127
128     mmap_n_desc = mmap_size / mmap_d_size;
129
130     return EFI_SUCCESS;
131 }
132
133 static EFI_STATUS
134 relocate_memory_map(void) {
135     if (!mmap_size) {
136         return EFI_LOAD_ERROR;
137     }
138
139     size_t mmap_n_desc = mmap_size / mmap_d_size;
140
141     for (size_t i= 0; i < mmap_n_desc; i++) {
142         EFI_MEMORY_DESCRIPTOR *desc =
143             (EFI_MEMORY_DESCRIPTOR *)(mmap + i * mmap_d_size);
144         // 1:1 mapping into kernel window
145         desc->VirtualStart = desc->PhysicalStart + KERNEL_OFFSET;
146     }
147
148     return EFI_SUCCESS;
149 }
150
151 static void
152 print_memory_map(int update)
153 {
154     if (update) {
155         update_memory_map();
156     }
157
158     size_t mmap_n_desc = mmap_size / mmap_d_size;
159
160     Print(L"Memory map at %lx, key: %x, descriptor version: %x\n",
161             mmap, mmap_key, mmap_d_ver);
162     Print(L"Got %d memory map entries of %dB (%dB).\n",
163                mmap_n_desc, mmap_d_size, mmap_size);
164
165     Print(L"Type          PStart           PEnd        "
166                "      Size       Attributes\n");
167     for (UINTN i = 0; i < mmap_n_desc; i++) {
168         EFI_MEMORY_DESCRIPTOR *desc = ((void *) mmap) + (mmap_d_size * i);
169
170         const char *description;
171         if (desc->Type < EfiMaxMemoryType) {
172             description= mmap_types[desc->Type];
173         }
174         else if (
175             EfiBarrelfishFirstMemType <= desc->Type &&
176             desc->Type < EfiBarrelfishMaxMemType
177         ) {
178             description = bf_mmap_types[desc->Type - EfiBarrelfishFirstMemType];
179         }
180         else {
181             description= "???";
182         }
183         
184         Print(L"%-13a %016lx %016lx %9ldkB %01x\n",
185               description,
186               desc->PhysicalStart,
187               desc->PhysicalStart + (desc->NumberOfPages << 12) - 1,
188               (desc->NumberOfPages << 12) / 1024,
189               desc->Attribute);
190     }
191 }
192
193 static lpaddr_t get_root_table(void) {
194     Print(L"Getting page table address\n");
195     uint64_t el, ttbr0;
196   __asm("mrs %0, currentel\n":"=r"(el));
197     el >>= 2;
198     Print(L"Currently in EL%lx\n", el);
199
200     if (el == 1) {
201       __asm("mrs %0, ttbr0_el1\n":"=r"(ttbr0));
202     } else if (el == 2) {                    // 2
203       __asm("mrs %0, ttbr0_el2\n":"=r"(ttbr0));
204     } else {
205         Print(L"Can't get page table\n");
206         return 0;
207     }
208     Print(L"Page table at 0x%lx\n", ttbr0);
209     return ttbr0;
210 }
211
212 static void dump_pages(void)
213 {
214     uint64_t i, j, k, l;
215
216     uint64_t el, ttbr0;
217   __asm("mrs %0, currentel\n":"=r"(el));
218     el = el / 4;
219
220     if (el == 1) {
221       __asm("mrs %0, ttbr0_el1\n":"=r"(ttbr0));
222     } else {                    // 2
223       __asm("mrs %0, ttbr0_el2\n":"=r"(ttbr0));
224     }
225     Print(L"el:%ld  ttbr0:%lx\n", el, ttbr0);
226
227     uint64_t *table0, *table1, *table2, *table3;
228     uint64_t offset0, offset1, offset2, offset3;
229
230     table0 = (uint64_t *) ttbr0;
231     for (i = 0; i < 512; i++) {
232         if (table0[i]) {
233             offset0 = i << 39;
234             Print(L"%016lx: %016lx\n", offset0, table0[i]);
235             if (!(table0[i] & 2)) {
236                 continue;
237             }
238             table1 = (uint64_t *) (table0[i] & 0x0000fffffffff000);
239             for (j = 0; j < 512; j++) {
240                 if (table1[j]) {
241                     offset1 = offset0 | (j << 30);
242                     Print(L"  %016lx: %016lx\n", offset1, table1[j]);
243                     if (!(table1[j] & 2)) {
244                         continue;
245                     }
246                     table2 = (uint64_t *) (table1[j] & 0x0000fffffffff000);
247                     for (k = 0; k < 512; k++) {
248                         if (table2[k]) {
249                             offset2 = offset1 | (k << 21);
250                             Print(L"    %016lx: %016lx\n", offset2,
251                                   table2[k]);
252                             if (!(table2[k] & 2)) {
253                                 continue;
254                             }
255                             table3 =
256                                 (uint64_t *) (table2[k] &
257                                               0x0000fffffffff000);
258                             for (l = 0; l < 512; l++) {
259                                 if (table3[l]) {
260                                     offset3 = offset2 | (l << 12);
261                                     Print(L"      %016lx: %016lx\n",
262                                           offset3, table3[l]);
263                                 }
264                             }
265                         }
266                     }
267                 }
268             }
269         }
270     }
271 }
272
273 static void
274 relocate_elf(EFI_PHYSICAL_ADDRESS segment_start, uint64_t virtual_offset,
275                  struct Blob_relocation *relocations,
276                  uint64_t no_relocations)
277 {
278     Print(L"Relocating ELF %lx %lx %d\n",
279         segment_start, relocations, no_relocations);
280     for (uint64_t i = 0; i < no_relocations; i++) {
281         *(uint64_t *)(segment_start + relocations[i].offset) =
282             segment_start + virtual_offset + relocations[i].addend;
283     }
284 }
285
286 static EFI_STATUS
287 relocate_boot_driver(struct Blob *blob_info, struct config *cfg)
288 {
289     EFI_STATUS status;
290
291     /* Should be page aligend */
292     ASSERT(blob_info->boot_driver_segment_size % BASE_PAGE_SIZE == 0);
293
294     EFI_PHYSICAL_ADDRESS boot_driver;
295     status = BS->AllocatePages(
296         AllocateAnyPages,
297         EfiBarrelfishCPUDriver,
298         blob_info->boot_driver_segment_size / BASE_PAGE_SIZE,
299         &boot_driver
300     );
301     if (EFI_ERROR(status)) {
302         Print(L"Error allocating memory for boot driver segment: %d\n", status);
303         return status;
304     }
305
306     memcpy((void *)boot_driver, BLOB_ADDRESS(blob_info->boot_driver_segment), blob_info->boot_driver_segment_size);
307
308     struct Blob_relocation *boot_driver_relocations =
309         (struct Blob_relocation *)BLOB_ADDRESS(blob_info->boot_driver_relocations);
310     relocate_elf(
311         boot_driver,
312         0,
313         boot_driver_relocations,
314         blob_info->boot_driver_relocations_count
315     );
316
317     cfg->boot_driver_entry = boot_driver + blob_info->boot_driver_entry;
318     
319     return EFI_SUCCESS;
320 }
321
322 static EFI_STATUS
323 relocate_cpu_driver(struct Blob *blob_info, struct config *cfg)
324 {
325     EFI_STATUS status;
326
327     /* Should be page aligend */
328     ASSERT(blob_info->cpu_driver_segment_size % BASE_PAGE_SIZE == 0);
329
330     EFI_PHYSICAL_ADDRESS cpu_driver;
331     status = BS->AllocatePages(
332         AllocateAnyPages,
333         EfiBarrelfishCPUDriver,
334         blob_info->cpu_driver_segment_size / BASE_PAGE_SIZE,
335         &cpu_driver
336     );
337     if (EFI_ERROR(status)) {
338         Print(L"Error allocating memory for CPU driver segment: %d\n", status);
339         return status;
340     }
341
342     memcpy((void *)cpu_driver, BLOB_ADDRESS(blob_info->cpu_driver_segment), blob_info->cpu_driver_segment_size);
343
344     struct Blob_relocation *cpu_driver_relocations =
345         (struct Blob_relocation *)BLOB_ADDRESS(blob_info->cpu_driver_relocations);
346     relocate_elf(
347         cpu_driver,
348         KERNEL_OFFSET,
349         cpu_driver_relocations,
350         blob_info->cpu_driver_relocations_count
351     );
352
353     cfg->cpu_driver_entry = cpu_driver + blob_info->cpu_driver_entry + KERNEL_OFFSET;
354
355     status = BS->AllocatePages(
356         AllocateAnyPages,
357         EfiBarrelfishCPUDriverStack,
358         KERNEL_STACK_SIZE / BASE_PAGE_SIZE,
359         &cfg->cpu_driver_stack
360     );
361     if (EFI_ERROR(status)) {
362         Print(L"Error allocating memory for CPU driver stack: %d\n", status);
363         return status;
364     }
365
366     cfg->cpu_driver_stack_size = KERNEL_STACK_SIZE;
367
368     Print(
369         L"Relocated CPU driver entry point is %lx, stack at %lx\n",
370         cfg->cpu_driver_entry,
371         cfg->cpu_driver_stack
372     );
373     
374     return EFI_SUCCESS;
375 }
376
377 static EFI_STATUS
378 relocate_modules(struct Blob *blob_info, struct config *cfg)
379 {
380     EFI_STATUS status;
381     
382     /* Should be page aligend */
383     ASSERT(blob_info->modules_size % BASE_PAGE_SIZE == 0);
384
385     status = BS->AllocatePages(
386         AllocateAnyPages,
387         EfiBarrelfishELFData,
388         blob_info->modules_size / BASE_PAGE_SIZE,
389         &cfg->modules
390     );
391     if (EFI_ERROR(status)) {
392         Print(L"Error allocating memory for modules: %d\n", status);
393         return status;
394     }
395
396     memcpy((void *)cfg->modules, BLOB_ADDRESS(blob_info->modules), blob_info->modules_size);
397     return EFI_SUCCESS;
398 }
399
400 static EFI_STATUS
401 relocate_multiboot(struct Blob *blob_info, struct config *cfg)
402 {
403     EFI_STATUS status;
404
405     /* Should be page aligend */
406     ASSERT(blob_info->multiboot_size % BASE_PAGE_SIZE == 0);
407
408     EFI_PHYSICAL_ADDRESS memory;
409     status = BS->AllocatePages(
410         AllocateAnyPages,
411         EfiBarrelfishMultibootData,
412         blob_info->multiboot_size / BASE_PAGE_SIZE,
413         (EFI_PHYSICAL_ADDRESS *)&cfg->multiboot
414     );
415     if (EFI_ERROR(status)) {
416         Print(L"Error allocating memory for multiboot info: %d\n", status);
417         return status;
418     }
419
420     memcpy(cfg->multiboot, BLOB_ADDRESS(blob_info->multiboot), blob_info->multiboot_size);
421
422     /* Module start & end pointed into the blob.
423      * Now they need to point into the module region
424      */
425     uint64_t module_offset = (uint64_t)cfg->modules - blob_info->modules;
426
427     Print(L"Relocating multiboot info: %lx\n", cfg->multiboot);
428     /* We don't have an end tag yet, but EFI mmap tag is last */
429     struct multiboot_tag *tag;
430     for (tag = cfg->multiboot->tags; tag->type != MULTIBOOT_TAG_TYPE_EFI_MMAP; tag = (void *)tag + tag->size) {
431         Print(L"%lx: tag %d:%d\n", tag, tag->type, tag->size);
432
433         if (tag->type == MULTIBOOT_TAG_TYPE_MODULE_64) {
434             struct multiboot_tag_module_64 *mtag = (struct multiboot_tag_module_64 *)tag;
435             Print(L"\tbefore %lx:%lx\n", mtag->mod_start, mtag->mod_end);
436             mtag->mod_start += module_offset;
437             mtag->mod_end += module_offset;
438             Print(L"\tafter  %lx:%lx\n", mtag->mod_start, mtag->mod_end);
439         }
440         else if (tag->type == MULTIBOOT_TAG_TYPE_CMDLINE) {
441             cfg->cmd_tag = (struct multiboot_tag_string *)tag;
442         }
443     }
444
445     ASSERT(tag->type == MULTIBOOT_TAG_TYPE_EFI_MMAP);
446     cfg->mmap_tag = (struct multiboot_tag_efi_mmap *)tag;
447
448     return EFI_SUCCESS;
449 }
450
451 static struct armv8_core_data *
452 create_core_data(struct config *cfg)
453 {
454     EFI_STATUS status;
455
456     struct armv8_core_data *core_data;
457     status = BS->AllocatePages(
458         AllocateAnyPages,
459         EfiBarrelfishCoreData,
460         1,
461         (EFI_PHYSICAL_ADDRESS *)&core_data
462     );
463     if (EFI_ERROR(status)) {
464         Print(L"Error allocating memory for core data: %d\n", status);
465         return NULL;
466     }
467
468     memset(core_data, 0, BASE_PAGE_SIZE);
469
470     core_data->boot_magic = ARMV8_BOOTMAGIC_BSP;
471     core_data->cpu_driver_stack =
472         (lpaddr_t)cfg->cpu_driver_stack + cfg->cpu_driver_stack_size - 16;
473     core_data->cpu_driver_stack_limit = (lpaddr_t)cfg->cpu_driver_stack;
474     core_data->cpu_driver_entry = (lvaddr_t)cfg->cpu_driver_entry;
475     core_data->page_table_root = get_root_table();
476     ntstring(
477         core_data->cpu_driver_cmdline,
478         cfg->cmd_tag->string,
479         MIN(
480             cfg->cmd_tag->size - sizeof(struct multiboot_tag_string),
481             sizeof(core_data->cpu_driver_cmdline) - 1
482         )
483     );
484     
485     core_data->multiboot_image.base = (lpaddr_t)cfg->multiboot;
486     core_data->multiboot_image.length = cfg->multiboot->total_size;
487     core_data->efi_mmap = (lpaddr_t)cfg->mmap_tag;
488
489     return core_data;
490 }
491
492 EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle,
493                            EFI_SYSTEM_TABLE * SystemTable)
494 {
495     EFI_STATUS status;
496
497     InitializeLib(ImageHandle, SystemTable);
498     
499     struct Blob *blob_info = (struct Blob *)BLOB_ADDRESS(0);
500     Print(L"Blob is at: 0x%lx\n", blob_info);
501     Print(L"Magic: %lx\n", blob_info->magic);
502
503     struct config cfg;
504
505     status = relocate_boot_driver(blob_info, &cfg);
506     if (EFI_ERROR(status)) {
507         Print(L"Failed to relocate boot driver\n");
508         return status;
509     }
510
511     status = relocate_cpu_driver(blob_info, &cfg);
512     if (EFI_ERROR(status)) {
513         Print(L"Failed to relocate CPU driver\n");
514         return status;
515     }
516
517     status = relocate_modules(blob_info, &cfg);
518     if (EFI_ERROR(status)) {
519         Print(L"Failed to relocate modules\n");
520         return status;
521     }
522
523     status = relocate_multiboot(blob_info, &cfg);
524     if (EFI_ERROR(status)) {
525         Print(L"Failed to relocate multiboot info\n");
526         return status;
527     }
528
529     struct armv8_core_data *core_data = create_core_data(&cfg);
530
531     Print(L"Terminating boot services and jumping to image at 0x%lx\n", cfg.boot_driver_entry);
532     Print(L"Core data pointer is %lx\n", core_data);
533
534     print_memory_map(1);
535
536     update_memory_map();
537
538     status = ST->BootServices->ExitBootServices(ImageHandle, mmap_key);
539     if (EFI_ERROR(status)) {
540         Print(L"Error exiting boot services: %d, %x\n", status, mmap_key);
541         return status;
542     }
543
544     /*** EFI boot services are now terminated, we're on our own. */
545     status = relocate_memory_map();
546     if (EFI_ERROR(status)) {
547         return EFI_SUCCESS;
548     }
549
550     /* The last thing we do is complete the multiboot info:
551      * Set the EFI mmap and end tag
552      */
553     cfg.mmap_tag->size = ROUND_UP(sizeof(struct multiboot_tag_efi_mmap) + mmap_size, 8);
554     cfg.mmap_tag->descr_size = mmap_d_size;
555     cfg.mmap_tag->descr_vers = mmap_d_ver;
556     memcpy(cfg.mmap_tag->efi_mmap, mmap, mmap_size);
557
558     struct multiboot_tag *end_tag = (void *)cfg.mmap_tag + cfg.mmap_tag->size;
559     end_tag->type = MULTIBOOT_TAG_TYPE_END;
560     end_tag->size = ROUND_UP(sizeof(struct multiboot_tag), 8);
561     cfg.multiboot->total_size = (void *)end_tag + end_tag->size - (void *)cfg.multiboot;
562
563     status = ST->RuntimeServices->SetVirtualAddressMap(
564         mmap_size,
565         mmap_d_size,
566         mmap_d_ver,
567         (void *) &mmap
568     );
569     if (EFI_ERROR(status)) {
570         return status;
571     }
572
573     // Jump to the bootloader, the blob can be reused
574     (*((boot_driver *) (cfg.boot_driver_entry))) (MULTIBOOT2_BOOTLOADER_MAGIC, core_data);
575
576     return EFI_SUCCESS;
577 }