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. */
44typedef uint64_t kvaddr_t;
45typedef uint64_t paddr_t;
46
47/*** A Linear Memory Allocator ***/
48static paddr_t phys_alloc_start = 0;
49
50static 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. */
57static 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. */
64static 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
74static 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
83static 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
96static void fail_elf(const char *s)
97{
98    fprintf(stderr, "%s: %s\n", s, elf_errmsg(elf_errno()));
99    exit(EXIT_FAILURE);
100}
101
102static 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
109struct ram_region {
110    uint64_t base;
111    uint64_t npages;
112    void *buffer;
113};
114
115struct loaded_module {
116    void *data;
117    paddr_t paddr;
118    size_t len, size;
119    const char *shortname;
120};
121
122struct 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. */
143void 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. */
176void *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.path) +
232                             strlen(menu->kernel.args) + 2);
233        sprintf(bootcmd->string, "%s %s", menu->kernel.path,
234                menu->kernel.args);
235        cursor += bootcmd->size;
236    }
237
238    /* Add the boot driver module. */
239    {
240        struct multiboot_tag_module_64 *boot_driver =
241            (struct multiboot_tag_module_64 *) cursor;
242
243        boot_driver->type = MULTIBOOT_TAG_TYPE_MODULE_64;
244        boot_driver->size = ALIGN(sizeof(struct multiboot_tag_module_64)
245                                  + strlen(menu->boot_driver.path) + 2);
246        boot_driver->mod_start = (multiboot_uint64_t) modules[0].paddr;
247        boot_driver->mod_end =
248            (multiboot_uint64_t) (modules[0].paddr + modules[0].size - 1);
249        sprintf(boot_driver->cmdline, "%s", menu->boot_driver.path);
250        cursor += boot_driver->size;
251    }
252    /* Add the kernel module. */
253    {
254        struct multiboot_tag_module_64 *kernel =
255            (struct multiboot_tag_module_64 *) cursor;
256
257        kernel->type = MULTIBOOT_TAG_TYPE_MODULE_64;
258        kernel->size = ALIGN(sizeof(struct multiboot_tag_module_64)
259                             + strlen(menu->kernel.path) +
260                             strlen(menu->kernel.args) + 2);
261        kernel->mod_start = (multiboot_uint64_t) modules[1].paddr;
262        kernel->mod_end =
263            (multiboot_uint64_t) (modules[1].paddr + modules[1].size - 1);
264        sprintf(kernel->cmdline, "%s %s", menu->kernel.path,
265                menu->kernel.args);
266        cursor += kernel->size;
267    }
268    /* Add the remaining modules */
269    for (i = 0; i < menu->nmodules; i++) {
270        struct multiboot_tag_module_64 *module =
271            (struct multiboot_tag_module_64 *) cursor;
272
273        module->type = MULTIBOOT_TAG_TYPE_MODULE_64;
274        module->size = ALIGN(sizeof(struct multiboot_tag_module_64)
275                             + strlen(menu->modules[i].path) +
276                             strlen(menu->modules[i].args) + 2);
277        module->mod_start = (multiboot_uint64_t) modules[i + 2].paddr;
278        module->mod_end =
279            (multiboot_uint64_t) (modules[i + 2].paddr +
280                                  modules[i + 2].size - 1);
281        sprintf(module->cmdline, "%s %s", menu->modules[i].path,
282                menu->modules[i].args);
283        cursor += module->size;
284    }
285    /* Add the EFI MMAP tag */
286    {
287        struct multiboot_tag_efi_mmap *mmap_tag =
288            (struct multiboot_tag_efi_mmap *) cursor;
289        mmap_tag->type = MULTIBOOT_TAG_TYPE_EFI_MMAP;
290        cursor += sizeof(struct multiboot_tag_efi_mmap);
291    }
292    return mb;
293}
294
295int relocate_elf(struct ram_region *segment, Elf * elf,
296                 Elf64_Phdr * phdr, size_t phnum, size_t shnum,
297                 unsigned *no_relocations,
298                 struct Blob_relocation **relocations)
299{
300    size_t i;
301
302    *no_relocations = 0;
303
304    /* Search for relocaton sections. */
305    for (i = 0; i < shnum; i++) {
306        Elf_Scn *scn = elf_getscn(elf, i);
307        if (!scn) {
308            printf("elf_getscn: %s\n", elf_errmsg(elf_errno()));
309            return -1;
310        }
311
312        Elf64_Shdr *shdr = elf64_getshdr(scn);
313        if (!shdr) {
314            printf("elf64_getshdr: %s\n", elf_errmsg(elf_errno()));
315            return -1;
316        }
317        if (shdr->sh_type == SHT_DYNAMIC) {
318            int relocations_size;
319            Elf_Data *data = elf_getdata(scn, NULL);
320            Elf64_Dyn *dt = (Elf64_Dyn *) data->d_buf;
321            for (; dt->d_tag && dt->d_tag != DT_RELACOUNT; dt++) {
322            }
323            assert(dt->d_tag == DT_RELACOUNT);
324            *no_relocations = dt->d_un.d_val;
325            relocations_size =
326                round_up(*no_relocations * sizeof(struct Blob_relocation),
327                         BASE_PAGE_SIZE);
328            *relocations = malloc(relocations_size);
329        } else if (shdr->sh_type == SHT_RELA) {
330            if (shdr->sh_info != 0) {
331                printf("I expected global relocations, but got"
332                       " section-specific ones.\n");
333                return -1;
334            }
335
336            /* Hardcoded for one loadable segment.
337               XXX: seems to be not always the case for some ARMv8 builids.
338             */
339            //ASSERT(phnum == 1);
340
341            Elf64_Addr segment_elf_base = phdr[0].p_vaddr;
342            Elf64_Addr segment_load_base = segment->base;
343            Elf64_Sxword segment_delta =
344                segment_load_base - segment_elf_base;
345
346            /* Walk the section data descriptors. */
347            Elf_Data *reldata;
348            for (reldata = elf_getdata(scn, NULL);
349                 reldata; reldata = elf_getdata(scn, reldata)) {
350                size_t rsize;
351                if (shdr->sh_type == SHT_REL)
352                    rsize = sizeof(Elf64_Rel);
353                else
354                    rsize = sizeof(Elf64_Rela);
355
356                size_t nrel = reldata->d_size / rsize;
357
358                /* Iterate through the relocations. */
359                size_t i;
360                for (i = 0; i < nrel; i++) {
361                    void *reladdr = reldata->d_buf + i * rsize;
362                    Elf64_Addr offset;
363                    Elf64_Xword sym, type;
364                    Elf64_Sxword addend;
365
366                    assert(shdr->sh_type == SHT_RELA);
367                    Elf64_Rela *rel = reladdr;
368
369                    offset = rel->r_offset;
370                    sym = ELF64_R_SYM(rel->r_info);
371                    type = ELF64_R_TYPE(rel->r_info);
372                    addend = rel->r_addend;
373
374                    assert(type == R_AARCH64_RELATIVE);
375                    if (sym != 0) {
376                        printf("Relocation references a"
377                               " dynamic symbol, which is"
378                               " unsupported.\n");
379                        return -1;
380                    }
381
382                    /* Delta(S) + A */
383                    (*relocations)[i].offset = offset;
384                    (*relocations)[i].addend = addend;
385                }
386            }
387        }
388    }
389
390    return 0;
391}
392
393/* Load and relocate an ELF, with the given offset between the physical
394 * address at which it is loaded, and the virtual address at which it
395 * executes.  For the boot driver, the offset is zero.  Return a variety of
396 * information about the loaded image. */
397static void load(struct loaded_module *module, uint32_t vp_offset,
398                 struct loaded_image *image, int save_sections)
399{
400    int i;
401    /* Open the ELF. */
402    Elf *elf = elf_memory(module->data, module->size);
403    if (!elf)
404        fail_elf("elf_begin");
405
406    /* Grab the unrelocated entry address from the header. */
407    Elf64_Ehdr *ehdr = elf64_getehdr(elf);
408    if (!ehdr)
409        fail_elf("elf64_getehdr");
410    image->entry = ehdr->e_entry;
411
412    /* Grab the program headers i.e. the list of loadable segments. */
413    size_t phnum;
414    if (elf_getphdrnum(elf, &phnum))
415        fail_elf("elf_getphnum");
416
417    Elf64_Phdr *phdr = elf64_getphdr(elf);
418    if (!phdr)
419        fail_elf("elf_getphdr");
420
421    DBG("%zd program segments.\n", phnum);
422
423    /* Grab the raw ELF data. */
424    size_t elfsize;
425    void *elfdata = elf_rawfile(elf, &elfsize);
426    if (!elfdata)
427        fail_elf("elf_rawfile");
428
429    /* Count the loadable segments, to allocate the region list. */
430    size_t nloadsegs = 0;
431    for (i = 0; i < phnum; i++) {
432        if (phdr[i].p_type == PT_LOAD)
433            nloadsegs++;
434    }
435
436    for (i = 0; i < phnum; i++) {
437        printf
438            ("Segment %d load address %zx, offset %zx, file size %zx, memory size %zx\n",
439             i, phdr[i].p_vaddr, phdr[i].p_offset, phdr[i].p_filesz,
440             phdr[i].p_memsz);
441        if (phdr[i].p_type != PT_LOAD)
442            continue;
443
444        unsigned p_pages =
445            round_up(phdr[i].p_memsz, BASE_PAGE_SIZE) / BASE_PAGE_SIZE;
446        void *p_buf;
447
448        paddr_t pa = phys_alloc(phdr[i].p_memsz, BASE_PAGE_SIZE);
449        p_buf = calloc(p_pages * BASE_PAGE_SIZE, 1);
450        assert(p_buf);
451
452        image->segment.buffer = p_buf;
453        image->segment.base = pa;
454        image->segment.npages = p_pages;
455
456        memcpy(p_buf, module->data + phdr[i].p_offset, phdr[i].p_filesz);
457    }
458
459    size_t shnum;
460    int status;
461    status = elf_getshdrnum(elf, &shnum);
462    if (status) {
463        printf("elf_getshdrnum: %s\n", elf_errmsg(elf_errno()));
464        assert(0);
465    }
466
467    status =
468        relocate_elf(&image->segment, elf, phdr, phnum, shnum,
469                     &image->no_relocations, &image->relocations);
470    if (status) {
471        printf("Relocation failed.\n");
472        assert(0);
473    }
474    elf_end(elf);
475}
476
477
478int main(int argc, char *argv[])
479{
480    char pathbuf[PATH_MAX + 1];
481
482    // if(argc != 6) usage(argv[0]);
483
484    const char *menu_lst = argv[1],
485        *outfile = argv[2], *buildroot = argv[3];
486
487    errno = 0;
488
489    printf("ARMv8 Static Bootloader\n");
490
491    /* Read the menu.lst file. */
492    printf("Reading boot configuration from %s\n", menu_lst);
493    struct menu_lst *menu = read_menu_lst(menu_lst);
494
495    struct loaded_module *modules =
496        calloc(menu->nmodules + 2, sizeof(struct loaded_module));
497    if (!modules)
498        fail_errno("calloc");
499
500    // create the Blob
501    paddr_t base = phys_alloc(sizeof(struct Blob), BASE_PAGE_SIZE);
502    printf("Blob info struct at PA %016lx\n", base);
503
504    // Load the boot driver
505    join_paths(pathbuf, buildroot, menu->boot_driver.path);
506    raw_load(pathbuf, modules);
507
508    /* Use the filename as a short identifier. */
509    const char *lastslash = strrchr(menu->boot_driver.path, '/');
510    if (lastslash) {
511        modules[0].shortname = lastslash + 1;
512    } else {
513        modules[0].shortname = "";
514    }
515    // Load the kernel
516    join_paths(pathbuf, buildroot, menu->kernel.path);
517    raw_load(pathbuf, modules + 1);
518
519    /* Use the filename as a short identifier. */
520    lastslash = strrchr(menu->kernel.path, '/');
521    if (lastslash) {
522        modules[1].shortname = lastslash + 1;
523    } else {
524        modules[1].shortname = "";
525    }
526
527    /*** Load the modules. ***/
528
529    for (size_t i = 0; i < menu->nmodules; i++) {
530        join_paths(pathbuf, buildroot, menu->modules[i].path);
531        raw_load(pathbuf, modules + i + 2);
532
533        /* Use the filename as a short identifier. */
534        lastslash = strrchr(menu->modules[i].path, '/');
535        if (lastslash) {
536            modules[i + 2].shortname = lastslash + 1;
537        } else {
538            modules[i + 2].shortname = "";
539        }
540    }
541
542    if (elf_version(EV_CURRENT) == EV_NONE)
543        fail("ELF library version out of date.\n");
544    /*** Load the boot driver. ***/
545
546    /* Load and relocate it. */
547    struct loaded_image bd_image[2];
548    bd_image[0].extrasym_name = "boot_arguments";
549    load(modules, 0, bd_image, 1);
550    load(modules + 1, 0, bd_image + 1, 1);
551
552    printf("Boot driver entry point: PA %08zx\n", bd_image[0].entry);
553    printf("CPU driver entry point: PA %08zx\n", bd_image[1].entry);
554
555    paddr_t pa, endpa;
556    struct Blob blob;
557
558    memset(blob.data, 0, sizeof(blob.data));
559    blob.magic = 0x12345678fedcba90;
560
561    pa = phys_alloc(bd_image[0].no_relocations *
562                    sizeof(struct Blob_relocation), BASE_PAGE_SIZE);
563    printf("Boot relocations PA %016zx,%d\n", pa,
564           bd_image[0].no_relocations);
565    blob.boot_driver_relocations = pa;
566    blob.boot_driver_relocations_count = bd_image[0].no_relocations;
567    blob.boot_driver_segment = bd_image[0].segment.base;
568    blob.boot_driver_segment_size = bd_image[0].segment.npages * BASE_PAGE_SIZE;
569    blob.boot_driver_entry = (uint64_t)bd_image[0].entry;
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    blob.cpu_driver_entry = (uint64_t)bd_image[1].entry;
580
581    /*** Create the multiboot info header. ***/
582    size_t mb_size, size;
583    paddr_t mb_base;
584    void *mb_image =
585        create_multiboot_info(menu, modules, &mb_size, &mb_base,
586                              bd_image[1].entry);
587
588    endpa = phys_alloc(BASE_PAGE_SIZE, BASE_PAGE_SIZE);
589    printf("Final PA %016zx\n", endpa);
590
591    blob.multiboot = mb_base;
592    blob.multiboot_size = mb_size;
593
594    blob.modules = modules[0].paddr;
595    for (size_t i = 0; i < menu->nmodules + 2; i++) {
596        blob.modules_size += modules[i].len;
597    }
598
599    size_t r;
600    FILE *fp = fopen(outfile, "wb");
601    assert(fp >= 0);
602    // write the blob info
603    r = fwrite(&blob, 1, BASE_PAGE_SIZE, fp);
604    assert(r == BASE_PAGE_SIZE);
605    // write the modules
606    for (size_t i = 0; i < menu->nmodules + 2; i++) {
607        r = fwrite(modules[i].data, 1, modules[i].len, fp);
608        assert(r == modules[i].len);
609    }
610    // write the boot driver's ELF section
611    r = fwrite(bd_image[0].segment.buffer, 1,
612              bd_image[0].segment.npages * BASE_PAGE_SIZE, fp);
613    assert(r == bd_image[0].segment.npages * BASE_PAGE_SIZE);
614    // write the kernel's ELF section
615    r = fwrite(bd_image[1].segment.buffer, 1,
616              bd_image[1].segment.npages * BASE_PAGE_SIZE, fp);
617    assert(r == bd_image[1].segment.npages * BASE_PAGE_SIZE);
618    // write the boot driver's relocations
619    size =
620        round_up(bd_image[0].no_relocations *
621                 sizeof(struct Blob_relocation), BASE_PAGE_SIZE);
622    r = fwrite(bd_image[0].relocations, 1, size, fp);
623    assert(r == size);
624    // write the kernel's relocations
625    size =
626        round_up(bd_image[1].no_relocations *
627                 sizeof(struct Blob_relocation), BASE_PAGE_SIZE);
628    r = fwrite(bd_image[1].relocations, 1, size, fp);
629    assert(r == size);
630    // write the multiboot info
631    r = fwrite(mb_image, 1, mb_size, fp);
632    assert(r == mb_size);
633    fclose(fp);
634
635    return 0;
636}
637