armv8: Refactor EFI loader
[barrelfish] / tools / armv8_bootimage / armv8_bootimage.c
1 /*
2  * Create a blob with a Multiboot2 image for the ARMv8 platform
3  *
4  * This tool reads menu.lst, loads a boot driver, a CPU kernel and modules,
5  * and assemble them into a Multiboot2 image. Adds also relocation info for
6  * the driver and the kernel.
7  *
8  * Copyright (c) 2016, 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 <stdio.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19
20 #include <sys/stat.h>
21 #include <sys/types.h>
22
23 #include <assert.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <libelf.h>
27 #include <limits.h>
28 #include <stdarg.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 /* We need to be able to parse menu.lst files, create multiboot images. */
36 #include "../../include/grubmenu.h"
37 #include "../../include/multiboot2.h"
38 #include "blob.h"
39
40 #define DBG(format, ...) printf(format, ## __VA_ARGS__)
41
42 /* Keep physical addresses and kernel virtual addresses separated, as far as
43  * possible. */
44 typedef uint64_t kvaddr_t;
45 typedef uint64_t paddr_t;
46
47 /*** A Linear Memory Allocator ***/
48 static paddr_t phys_alloc_start = 0;
49
50 static size_t round_up(size_t x, size_t y)
51 {
52     size_t z = x + (y - 1);
53     return z - (z % y);
54 }
55
56 /* Advance the allocator to an address with the given alignment. */
57 static paddr_t align_alloc(paddr_t align)
58 {
59     phys_alloc_start = round_up(phys_alloc_start, align);
60     return phys_alloc_start;
61 }
62
63 /* Allocate an aligned block. */
64 static paddr_t phys_alloc(size_t size, size_t align)
65 {
66     align_alloc(align);
67     paddr_t addr = phys_alloc_start;
68     phys_alloc_start += size;
69     return addr;
70 }
71
72 /*** Failure Handling ***/
73
74 static void fail(const char *fmt, ...)
75 {
76     va_list ap;
77     va_start(ap, fmt);
78     vfprintf(stderr, fmt, ap);
79     va_end(ap);
80     exit(EXIT_FAILURE);
81 }
82
83 static void fail_errno(const char *fmt, ...)
84 {
85     char s[1024];
86
87     va_list ap;
88     va_start(ap, fmt);
89     vsnprintf(s, 1024, fmt, ap);
90     va_end(ap);
91
92     perror(s);
93     exit(EXIT_FAILURE);
94 }
95
96 static void fail_elf(const char *s)
97 {
98     fprintf(stderr, "%s: %s\n", s, elf_errmsg(elf_errno()));
99     exit(EXIT_FAILURE);
100 }
101
102 static void join_paths(char *dst, const char *src1, const char *src2)
103 {
104     strcpy(dst, src1);
105     dst[strlen(src1)] = '/';
106     strcpy(dst + strlen(src1) + 1, src2);
107 }
108
109 struct ram_region {
110     uint64_t base;
111     uint64_t npages;
112     void *buffer;
113 };
114
115 struct loaded_module {
116     void *data;
117     paddr_t paddr;
118     size_t len, size;
119     const char *shortname;
120 };
121
122 struct loaded_image {
123     struct ram_region segment;
124
125     size_t loaded_size;
126     paddr_t loaded_paddr;
127     kvaddr_t loaded_vaddr;
128
129     paddr_t entry;
130     const char *extrasym_name;
131     void *extrasym_ptr;
132
133     void *shdrs, *symtab, *strtab, *shstrtab;
134     size_t shdrs_size, symtab_size, strtab_size, shstrtab_size;
135     size_t shdrs_entsize, symtab_entsize;
136
137     unsigned no_relocations;
138     struct Blob_relocation *relocations;
139 };
140
141
142 /* Load an ELF file as a raw data blob. */
143 void raw_load(const char *path, struct loaded_module *m)
144 {
145     struct stat mstat;
146
147     if (stat(path, &mstat))
148         fail_errno("stat: %s", path);
149
150     size_t data_len = mstat.st_size;
151     m->len = round_up(data_len, BASE_PAGE_SIZE);
152     m->size = data_len;
153     m->data = calloc(m->len, 1);
154     if (!m->data)
155         fail_errno("calloc");
156     m->paddr = phys_alloc(m->len, BASE_PAGE_SIZE);
157
158     printf("Allocated 0x%zx at PA %016zx for %s (%zd)\n", m->len, m->paddr,
159            path, data_len);
160
161     int fd = open(path, O_RDONLY);
162     if (fd < 0)
163         fail_errno("open: %s", path);
164     size_t read_len = read(fd, m->data, data_len);
165     if (read_len != data_len)
166         fail_errno("fread");
167     close(fd);
168 }
169
170 /*** Multiboot ***/
171
172 #define ROUND_UP(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
173 #define ALIGN(x) ROUND_UP((x), sizeof(uintptr_t))
174
175 /* Create the multiboot header, using only *physical* addresses. */
176 void *create_multiboot_info(struct menu_lst *menu,
177                             struct loaded_module *modules,
178                             size_t * mb_size, paddr_t * mb_base,
179                             paddr_t entry)
180 {
181     size_t size;
182     unsigned i;
183     void *cursor;
184
185     /* Calculate the boot information size. */
186     /* Multiboot2 information data structure */
187     size = 8;
188     /* cpu driver command line */
189     size += ALIGN(sizeof(struct multiboot_tag_string)
190                   + strlen(menu->kernel.args) + 1);
191     // /* Boot driver module tag, including command line and ELF image */
192     size += ALIGN(sizeof(struct multiboot_tag_module_64)
193                   + strlen(menu->boot_driver.path) + 2);
194     // /* CPU driver module tag, including command line and ELF image */
195     size += ALIGN(sizeof(struct multiboot_tag_module_64)
196                   + strlen(menu->kernel.path) + strlen(menu->kernel.args) +
197                   2);
198     /* All other modules */
199     for (i = 0; i < menu->nmodules; i++) {
200         size += ALIGN(sizeof(struct multiboot_tag_module_64)
201                       + strlen(menu->modules[i].path) +
202                       strlen(menu->modules[i].args) + 2);
203     }
204 #define MEM_MAP_SIZE (1<<13)
205     /* EFI memory map */
206     size += ALIGN(sizeof(struct multiboot_tag_efi_mmap) + MEM_MAP_SIZE);
207     // END tag
208     size += ALIGN(sizeof(struct multiboot_tag));
209
210     size_t allocated_size = round_up(size, BASE_PAGE_SIZE);
211     /* Allocate target addresses. */
212     paddr_t base = phys_alloc(size, BASE_PAGE_SIZE);
213     *mb_size = allocated_size;
214     *mb_base = base;
215
216     /* Allocate our host buffer. */
217     void *mb = calloc(allocated_size, 1);
218     if (!mb)
219         fail_errno("calloc");
220
221     cursor = mb;
222     /* Skip the information structure for now */
223     cursor += 8;
224
225     /* Add the boot command line */
226     {
227         struct multiboot_tag_string *bootcmd =
228             (struct multiboot_tag_string *) cursor;
229         bootcmd->type = MULTIBOOT_TAG_TYPE_CMDLINE;
230         bootcmd->size = ALIGN(sizeof(struct multiboot_tag_string)
231                               + strlen(menu->kernel.args) + 1);
232         strcpy(bootcmd->string, menu->kernel.args);
233         cursor += bootcmd->size;
234     }
235
236     /* Add the boot driver module. */
237     {
238         struct multiboot_tag_module_64 *boot_driver =
239             (struct multiboot_tag_module_64 *) cursor;
240
241         boot_driver->type = MULTIBOOT_TAG_TYPE_MODULE_64;
242         boot_driver->size = ALIGN(sizeof(struct multiboot_tag_module_64)
243                                   + strlen(menu->boot_driver.path) + 2);
244         boot_driver->mod_start = (multiboot_uint64_t) modules[0].paddr;
245         boot_driver->mod_end =
246             (multiboot_uint64_t) (modules[0].paddr + modules[0].size - 1);
247         sprintf(boot_driver->cmdline, "%s", menu->boot_driver.path);
248         cursor += boot_driver->size;
249     }
250     /* Add the kernel module. */
251     {
252         struct multiboot_tag_module_64 *kernel =
253             (struct multiboot_tag_module_64 *) cursor;
254
255         kernel->type = MULTIBOOT_TAG_TYPE_MODULE_64;
256         kernel->size = ALIGN(sizeof(struct multiboot_tag_module_64)
257                              + strlen(menu->kernel.path) +
258                              strlen(menu->kernel.args) + 2);
259         kernel->mod_start = (multiboot_uint64_t) modules[1].paddr;
260         kernel->mod_end =
261             (multiboot_uint64_t) (modules[1].paddr + modules[1].size - 1);
262         sprintf(kernel->cmdline, "%s %s", menu->kernel.path,
263                 menu->kernel.args);
264         cursor += kernel->size;
265     }
266     /* Add the remaining modules */
267     for (i = 0; i < menu->nmodules; i++) {
268         struct multiboot_tag_module_64 *module =
269             (struct multiboot_tag_module_64 *) cursor;
270
271         module->type = MULTIBOOT_TAG_TYPE_MODULE_64;
272         module->size = ALIGN(sizeof(struct multiboot_tag_module_64)
273                              + strlen(menu->modules[i].path) +
274                              strlen(menu->modules[i].args) + 2);
275         module->mod_start = (multiboot_uint64_t) modules[i + 2].paddr;
276         module->mod_end =
277             (multiboot_uint64_t) (modules[i + 2].paddr +
278                                   modules[i + 2].size - 1);
279         sprintf(module->cmdline, "%s %s", menu->modules[i].path,
280                 menu->modules[i].args);
281         cursor += module->size;
282     }
283     /* Add the EFI MMAP tag */
284     {
285         struct multiboot_tag_efi_mmap *mmap_tag =
286             (struct multiboot_tag_efi_mmap *) cursor;
287         mmap_tag->type = MULTIBOOT_TAG_TYPE_EFI_MMAP;
288         cursor += sizeof(struct multiboot_tag_efi_mmap);
289     }
290     return mb;
291 }
292
293 int relocate_elf(struct ram_region *segment, Elf * elf,
294                  Elf64_Phdr * phdr, size_t phnum, size_t shnum,
295                  unsigned *no_relocations,
296                  struct Blob_relocation **relocations)
297 {
298     size_t i;
299
300     *no_relocations = 0;
301
302     /* Search for relocaton sections. */
303     for (i = 0; i < shnum; i++) {
304         Elf_Scn *scn = elf_getscn(elf, i);
305         if (!scn) {
306             printf("elf_getscn: %s\n", elf_errmsg(elf_errno()));
307             return -1;
308         }
309
310         Elf64_Shdr *shdr = elf64_getshdr(scn);
311         if (!shdr) {
312             printf("elf64_getshdr: %s\n", elf_errmsg(elf_errno()));
313             return -1;
314         }
315         if (shdr->sh_type == SHT_DYNAMIC) {
316             int relocations_size;
317             Elf_Data *data = elf_getdata(scn, NULL);
318             Elf64_Dyn *dt = (Elf64_Dyn *) data->d_buf;
319             for (; dt->d_tag && dt->d_tag != DT_RELACOUNT; dt++) {
320             }
321             assert(dt->d_tag == DT_RELACOUNT);
322             *no_relocations = dt->d_un.d_val;
323             relocations_size =
324                 round_up(*no_relocations * sizeof(struct Blob_relocation),
325                          BASE_PAGE_SIZE);
326             *relocations = malloc(relocations_size);
327         } else if (shdr->sh_type == SHT_RELA) {
328             if (shdr->sh_info != 0) {
329                 printf("I expected global relocations, but got"
330                        " section-specific ones.\n");
331                 return -1;
332             }
333
334             /* Hardcoded for one loadable segment.
335                XXX: seems to be not always the case for some ARMv8 builids.
336              */
337             //ASSERT(phnum == 1);
338
339             Elf64_Addr segment_elf_base = phdr[0].p_vaddr;
340             Elf64_Addr segment_load_base = segment->base;
341             Elf64_Sxword segment_delta =
342                 segment_load_base - segment_elf_base;
343
344             /* Walk the section data descriptors. */
345             Elf_Data *reldata;
346             for (reldata = elf_getdata(scn, NULL);
347                  reldata; reldata = elf_getdata(scn, reldata)) {
348                 size_t rsize;
349                 if (shdr->sh_type == SHT_REL)
350                     rsize = sizeof(Elf64_Rel);
351                 else
352                     rsize = sizeof(Elf64_Rela);
353
354                 size_t nrel = reldata->d_size / rsize;
355
356                 /* Iterate through the relocations. */
357                 size_t i;
358                 for (i = 0; i < nrel; i++) {
359                     void *reladdr = reldata->d_buf + i * rsize;
360                     Elf64_Addr offset;
361                     Elf64_Xword sym, type;
362                     Elf64_Sxword addend;
363
364                     assert(shdr->sh_type == SHT_RELA);
365                     Elf64_Rela *rel = reladdr;
366
367                     offset = rel->r_offset;
368                     sym = ELF64_R_SYM(rel->r_info);
369                     type = ELF64_R_TYPE(rel->r_info);
370                     addend = rel->r_addend;
371
372                     assert(type == R_AARCH64_RELATIVE);
373                     if (sym != 0) {
374                         printf("Relocation references a"
375                                " dynamic symbol, which is"
376                                " unsupported.\n");
377                         return -1;
378                     }
379
380                     /* Delta(S) + A */
381                     (*relocations)[i].offset = offset;
382                     (*relocations)[i].addend = addend;
383                 }
384             }
385         }
386     }
387
388     return 0;
389 }
390
391 /* Load and relocate an ELF, with the given offset between the physical
392  * address at which it is loaded, and the virtual address at which it
393  * executes.  For the boot driver, the offset is zero.  Return a variety of
394  * information about the loaded image. */
395 static void load(struct loaded_module *module, uint32_t vp_offset,
396                  struct loaded_image *image, int save_sections)
397 {
398     int i;
399     /* Open the ELF. */
400     Elf *elf = elf_memory(module->data, module->size);
401     if (!elf)
402         fail_elf("elf_begin");
403
404     /* Grab the unrelocated entry address from the header. */
405     Elf64_Ehdr *ehdr = elf64_getehdr(elf);
406     if (!ehdr)
407         fail_elf("elf64_getehdr");
408     image->entry = ehdr->e_entry;
409
410     /* Grab the program headers i.e. the list of loadable segments. */
411     size_t phnum;
412     if (elf_getphdrnum(elf, &phnum))
413         fail_elf("elf_getphnum");
414
415     Elf64_Phdr *phdr = elf64_getphdr(elf);
416     if (!phdr)
417         fail_elf("elf_getphdr");
418
419     DBG("%zd program segments.\n", phnum);
420
421     /* Grab the raw ELF data. */
422     size_t elfsize;
423     void *elfdata = elf_rawfile(elf, &elfsize);
424     if (!elfdata)
425         fail_elf("elf_rawfile");
426
427     /* Count the loadable segments, to allocate the region list. */
428     size_t nloadsegs = 0;
429     for (i = 0; i < phnum; i++) {
430         if (phdr[i].p_type == PT_LOAD)
431             nloadsegs++;
432     }
433
434     for (i = 0; i < phnum; i++) {
435         printf
436             ("Segment %d load address %zx, offset %zx, file size %zx, memory size %zx\n",
437              i, phdr[i].p_vaddr, phdr[i].p_offset, phdr[i].p_filesz,
438              phdr[i].p_memsz);
439         if (phdr[i].p_type != PT_LOAD)
440             continue;
441
442         unsigned p_pages =
443             round_up(phdr[i].p_memsz, BASE_PAGE_SIZE) / BASE_PAGE_SIZE;
444         void *p_buf;
445
446         paddr_t pa = phys_alloc(phdr[i].p_memsz, BASE_PAGE_SIZE);
447         p_buf = calloc(p_pages * BASE_PAGE_SIZE, 1);
448         assert(p_buf);
449
450         image->segment.buffer = p_buf;
451         image->segment.base = pa;
452         image->segment.npages = p_pages;
453
454         memcpy(p_buf, module->data + phdr[i].p_offset, phdr[i].p_filesz);
455     }
456
457     size_t shnum;
458     int status;
459     status = elf_getshdrnum(elf, &shnum);
460     if (status) {
461         printf("elf_getshdrnum: %s\n", elf_errmsg(elf_errno()));
462         assert(0);
463     }
464
465     status =
466         relocate_elf(&image->segment, elf, phdr, phnum, shnum,
467                      &image->no_relocations, &image->relocations);
468     if (status) {
469         printf("Relocation failed.\n");
470         assert(0);
471     }
472     elf_end(elf);
473 }
474
475
476 int main(int argc, char *argv[])
477 {
478     char pathbuf[PATH_MAX + 1];
479
480     // if(argc != 6) usage(argv[0]);
481
482     const char *menu_lst = argv[1],
483         *outfile = argv[2], *buildroot = argv[3];
484
485     errno = 0;
486
487     printf("ARMv8 Static Bootloader\n");
488
489     /* Read the menu.lst file. */
490     printf("Reading boot configuration from %s\n", menu_lst);
491     struct menu_lst *menu = read_menu_lst(menu_lst);
492
493     struct loaded_module *modules =
494         calloc(menu->nmodules + 2, sizeof(struct loaded_module));
495     if (!modules)
496         fail_errno("calloc");
497
498     // create the Blob
499     paddr_t base = phys_alloc(sizeof(struct Blob), BASE_PAGE_SIZE);
500     printf("Blob info struct at PA %016lx\n", base);
501
502     // Load the boot driver
503     join_paths(pathbuf, buildroot, menu->boot_driver.path);
504     raw_load(pathbuf, modules);
505
506     /* Use the filename as a short identifier. */
507     const char *lastslash = strrchr(menu->boot_driver.path, '/');
508     if (lastslash) {
509         modules[0].shortname = lastslash + 1;
510     } else {
511         modules[0].shortname = "";
512     }
513     // Load the kernel
514     join_paths(pathbuf, buildroot, menu->kernel.path);
515     raw_load(pathbuf, modules + 1);
516
517     /* Use the filename as a short identifier. */
518     lastslash = strrchr(menu->kernel.path, '/');
519     if (lastslash) {
520         modules[1].shortname = lastslash + 1;
521     } else {
522         modules[1].shortname = "";
523     }
524
525     /*** Load the modules. ***/
526
527     for (size_t i = 0; i < menu->nmodules; i++) {
528         join_paths(pathbuf, buildroot, menu->modules[i].path);
529         raw_load(pathbuf, modules + i + 2);
530
531         /* Use the filename as a short identifier. */
532         lastslash = strrchr(menu->modules[i].path, '/');
533         if (lastslash) {
534             modules[i + 2].shortname = lastslash + 1;
535         } else {
536             modules[i + 2].shortname = "";
537         }
538     }
539
540     if (elf_version(EV_CURRENT) == EV_NONE)
541         fail("ELF library version out of date.\n");
542     /*** Load the boot driver. ***/
543
544     /* Load and relocate it. */
545     struct loaded_image bd_image[2];
546     bd_image[0].extrasym_name = "boot_arguments";
547     load(modules, 0, bd_image, 1);
548     load(modules + 1, 0, bd_image + 1, 1);
549
550     printf("Boot driver entry point: PA %08zx\n", bd_image[0].entry);
551     printf("CPU driver entry point: PA %08zx\n", bd_image[1].entry);
552
553     paddr_t pa, endpa;
554     struct Blob blob;
555
556     memset(blob.data, 0, sizeof(blob.data));
557
558     for (size_t i = 0; i < menu->nmodules + 2; i++) {
559         blob.modules_size += modules[i].len;
560     }
561
562     pa = phys_alloc(bd_image[0].no_relocations *
563                     sizeof(struct Blob_relocation), BASE_PAGE_SIZE);
564     printf("Boot relocations PA %016zx,%d\n", pa,
565            bd_image[0].no_relocations);
566     blob.boot_driver_relocations = pa;
567     blob.boot_driver_relocations_count = bd_image[0].no_relocations;
568     blob.boot_driver_segment = bd_image[0].segment.base;
569     blob.boot_driver_segment_size = bd_image[0].segment.npages * BASE_PAGE_SIZE;
570
571     pa = phys_alloc(bd_image[1].no_relocations *
572                     sizeof(struct Blob_relocation), BASE_PAGE_SIZE);
573     printf("Kernel relocations PA %016zx,%d\n", pa,
574            bd_image[1].no_relocations);
575     blob.cpu_driver_relocations = pa;
576     blob.cpu_driver_relocations_count = bd_image[1].no_relocations;
577     blob.cpu_driver_segment = bd_image[1].segment.base;
578     blob.cpu_driver_segment_size = bd_image[1].segment.npages * BASE_PAGE_SIZE;
579
580     /*** Create the multiboot info header. ***/
581     size_t mb_size, size;
582     paddr_t mb_base;
583     void *mb_image =
584         create_multiboot_info(menu, modules, &mb_size, &mb_base,
585                               bd_image[1].entry);
586
587     endpa = phys_alloc(BASE_PAGE_SIZE, BASE_PAGE_SIZE);
588     printf("Final PA %016zx\n", endpa);
589
590     blob.magic = 0x12345678fedcba90;
591     blob.multiboot = mb_base;
592     blob.multiboot_size = mb_size;
593     blob.boot_driver_entry = (paddr_t) bd_image[0].entry;
594     blob.cpu_driver_entry = (paddr_t) bd_image[1].entry;
595
596     size_t r;
597     int fd = open(outfile, O_CREAT | O_WRONLY,
598                   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
599     assert(fd >= 0);
600     // write the blob info
601     r = write(fd, &blob, BASE_PAGE_SIZE);
602     assert(r == BASE_PAGE_SIZE);
603     // write the modules
604     for (size_t i = 0; i < menu->nmodules + 2; i++) {
605         r = write(fd, modules[i].data, modules[i].len);
606         assert(r == modules[i].len);
607     }
608     // write the boot driver's ELF section
609     r = write(fd, bd_image[0].segment.buffer,
610               bd_image[0].segment.npages * BASE_PAGE_SIZE);
611     assert(r == bd_image[0].segment.npages * BASE_PAGE_SIZE);
612     // write the kernel's ELF section
613     r = write(fd, bd_image[1].segment.buffer,
614               bd_image[1].segment.npages * BASE_PAGE_SIZE);
615     assert(r == bd_image[1].segment.npages * BASE_PAGE_SIZE);
616     // write the boot driver's relocations
617     size =
618         round_up(bd_image[0].no_relocations *
619                  sizeof(struct Blob_relocation), BASE_PAGE_SIZE);
620     r = write(fd, bd_image[0].relocations, size);
621     assert(r == size);
622     // write the kernel's relocations
623     size =
624         round_up(bd_image[1].no_relocations *
625                  sizeof(struct Blob_relocation), BASE_PAGE_SIZE);
626     r = write(fd, bd_image[1].relocations, size);
627     assert(r == size);
628     // write the multiboot info
629     r = write(fd, mb_image, mb_size);
630     assert(r == mb_size);
631     close(fd);
632
633     return 0;
634 }