1#include <libelf.h> /* FreeBSD libelf */
2#include <multiboot2.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7#include "config.h"
8#include "util.h"
9
10void *
11create_multiboot2_info(struct config *cfg, Elf *elf, size_t mmap_size) {
12    size_t size;
13    struct component_config *cmp;
14    void *cursor;
15
16    size_t shnum;
17    if(elf_getshdrnum(elf, &shnum)) {
18        fprintf(stderr, "elf_getshdrnum: %s\n",
19                elf_errmsg(elf_errno()));
20        return NULL;
21    }
22
23    /* Calculate the boot information size. */
24    /* Fixed header - there's no struct for this in multiboot.h */
25    size= 8;
26    /* Kernel command line */
27    size+= sizeof(struct multiboot_tag_string)
28         + cfg->kernel->args_len+1;
29    /* ELF section headers */
30    size+= sizeof(struct multiboot_tag_elf_sections)
31         + shnum * sizeof(Elf64_Shdr);
32    /* Kernel module tag, including command line. */
33    size+= sizeof(struct multiboot_tag_module_64)
34         + cfg->kernel->args_len+1;
35    /* All other modules */
36    for(cmp= cfg->first_module; cmp; cmp= cmp->next) {
37        size+= sizeof(struct multiboot_tag_module_64)
38             + cmp->args_len+1;
39    }
40    /* EFI memory map */
41    size+= sizeof(struct multiboot_tag_efi_mmap)
42         + mmap_size;
43
44    cfg->multiboot_size= size;
45    cfg->multiboot_alloc= ROUNDUP(size, PAGE_4k);
46
47    cfg->multiboot= calloc(1, cfg->multiboot_alloc);
48    if(!cfg->multiboot) {
49        perror("calloc");
50        return NULL;
51    }
52
53    cursor= cfg->multiboot;
54    /* Write the fixed header. */
55    *((uint32_t *)cursor)= size; /* total_size */
56    cursor+= sizeof(uint32_t);
57    *((uint32_t *)cursor)= 0;    /* reserved */
58    cursor+= sizeof(uint32_t);
59
60    /* Add the boot command line */
61    {
62        struct multiboot_tag_string *bootcmd=
63            (struct multiboot_tag_string *)cursor;
64
65        bootcmd->type= MULTIBOOT_TAG_TYPE_CMDLINE;
66        bootcmd->size= sizeof(struct multiboot_tag_string)
67                     + cfg->kernel->args_len+1;
68        ntstring(bootcmd->string,
69                 cfg->buf + cfg->kernel->args_start,
70                 cfg->kernel->args_len);
71
72        cursor+= sizeof(struct multiboot_tag_string)
73               + cfg->kernel->args_len+1;
74    }
75    /* Add the ELF section headers. */
76    {
77        struct multiboot_tag_elf_sections *sections=
78            (struct multiboot_tag_elf_sections *)cursor;
79
80        size_t shndx;
81        if(elf_getshdrstrndx(elf, &shndx)) {
82            fprintf(stderr, "elf_getshdrstrndx: %s\n",
83                    elf_errmsg(elf_errno()));
84            return NULL;
85        }
86
87        sections->type= MULTIBOOT_TAG_TYPE_ELF_SECTIONS;
88        sections->size= sizeof(struct multiboot_tag_elf_sections)
89                 + shnum * sizeof(Elf64_Shdr);
90        sections->num= shnum;
91        sections->entsize= sizeof(Elf64_Shdr);
92        sections->shndx= shndx;
93
94        Elf64_Shdr *shdrs= (Elf64_Shdr *)sections->sections;
95        for(size_t i= 0; i < shnum; i++) {
96            Elf_Scn *scn= elf_getscn(elf, i);
97            if(!scn) {
98                fprintf(stderr, "elf_getscn: %s\n",
99                        elf_errmsg(elf_errno()));
100                return NULL;
101            }
102
103            Elf64_Shdr *shdr= elf64_getshdr(scn);
104            if(!shdr) {
105                fprintf(stderr, "elf64_getshdr: %s\n",
106                        elf_errmsg(elf_errno()));
107                return NULL;
108            }
109
110            memcpy(&shdrs[i], shdr, sizeof(Elf64_Shdr));
111        }
112
113        cursor+= sizeof(struct multiboot_tag_elf_sections)
114               + shnum * sizeof(Elf64_Shdr);
115    }
116    /* Add the kernel module. */
117    {
118        struct multiboot_tag_module_64 *kernel=
119            (struct multiboot_tag_module_64 *)cursor;
120
121        kernel->type= MULTIBOOT_TAG_TYPE_MODULE_64;
122        kernel->size= sizeof(struct multiboot_tag_module_64)
123                    + cfg->kernel->args_len+1;
124        /* Leave the addresses uninitialised until we've finished allocation,
125         * which needs the multboot image constructed first. */
126        ntstring(kernel->cmdline,
127                 cfg->buf + cfg->kernel->args_start,
128                 cfg->kernel->args_len);
129
130        cfg->kernel->tag= kernel;
131
132        cursor+= sizeof(struct multiboot_tag_module_64)
133               + cfg->kernel->args_len+1;
134    }
135    /* Add the remaining modules */
136    for(cmp= cfg->first_module; cmp; cmp= cmp->next) {
137        struct multiboot_tag_module_64 *module=
138            (struct multiboot_tag_module_64 *)cursor;
139
140        module->type= MULTIBOOT_TAG_TYPE_MODULE_64;
141        module->size= sizeof(struct multiboot_tag_module_64)
142                    + cmp->args_len+1;
143        ntstring(module->cmdline, cfg->buf + cmp->args_start, cmp->args_len);
144
145        cmp->tag= module;
146
147        cursor+= sizeof(struct multiboot_tag_module_64)
148               + cmp->args_len+1;
149    }
150    /* Record the position of the memory map, to be filled in after we've
151     * finished doing allocations. */
152    cfg->mmap_tag= (struct multiboot_tag_efi_mmap *)cursor;
153    cursor+= sizeof(struct multiboot_tag_efi_mmap);
154    cfg->mmap_start= cursor;
155
156    return cfg->multiboot;
157}
158