1/**
2 * \file
3 * \brief Elver - Intermediary stage bootloader
4 *
5 * Elver is used to switch the system into 64-bit long-mode and load
6 * the kernel, which is a relocatable ELF64 image. Unfortunately, GRUB
7 * is not able to this without a patch. This is purely for
8 * backwards-compatibility. As soon as bootloaders support loading
9 * relocatable ELF64 images into 64-bit mode, this can be dropped.
10 */
11
12/*
13 * Copyright (c) 2007, 2008, 2009, 2010, 2013, ETH Zurich.
14 * All rights reserved.
15 *
16 * This file is distributed under the terms in the attached LICENSE file.
17 * If you do not find this file, copies can be found by writing to:
18 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
19 */
20
21#include <stdio.h>
22#include <stdint.h>
23#include <stddef.h>
24#include <string.h>
25#include <barrelfish_kpi/types.h>
26#include <errors/errno.h>
27#include <elf/elf.h>
28#include <multiboot.h>
29
30#define PTABLE_EXECUTE_DISABLE  (1LL << 63)
31#define PTABLE_GLOBAL_PAGE      (1L << 8)
32#define PTABLE_ATTR_INDEX       (1L << 7)
33#define PTABLE_DIRTY            (1L << 6)
34#define PTABLE_ACCESSED         (1L << 5)
35#define PTABLE_CACHE_DISABLED   (1L << 4)
36#define PTABLE_WRITE_THROUGH    (1L << 3)
37#define PTABLE_USER_SUPERVISOR  (1L << 2)
38#define PTABLE_READ_WRITE       (1L << 1)
39#define PTABLE_PRESENT          (1L << 0)
40
41#define BASE_PAGE_SIZE                  0x1000
42#define LARGE_PAGE_SIZE                 0x200000
43#define ONE_GIB                         (1<<30)
44
45#define PTABLE_SIZE             512     /**< Page directory/table size */
46#define PTABLE_MASK             0x1ff   /**< Page dir/table address mask */
47#define PTABLE_CLEAR            0       /**< Bitmap of a clear table entry */
48
49#define PML4_BASE(base)         (((uint64_t)(base) >> 39) & PTABLE_MASK)
50#define PDPT_BASE(base)         (((uint64_t)(base) >> 30) & PTABLE_MASK)
51#define PDIR_BASE(base)         (((uint64_t)(base) >> 21) & PTABLE_MASK)
52#define PTABLE_BASE(base)       (((uint64_t)(base) >> 12) & PTABLE_MASK)
53
54/// Round up n to the next multiple of size
55#define ROUND_UP(n, size)           ((((n) + (size) - 1)) & (~((size) - 1)))
56
57union pdir_entry {
58    uint64_t raw;
59    struct {
60        uint64_t        present         :1;
61        uint64_t        read_write      :1;
62        uint64_t        user_supervisor :1;
63        uint64_t        write_through   :1;
64        uint64_t        cache_disabled  :1;
65        uint64_t        accessed        :1;
66        uint64_t        reserved        :3;
67        uint64_t        available       :3;
68        uint64_t        base_addr       :28;
69        uint64_t        reserved2       :12;
70        uint64_t        available2      :11;
71        uint64_t        execute_disable :1;
72    } d;
73};
74
75/**
76 * A page table entry.
77 */
78union ptable_entry {
79    uint64_t raw;
80    struct {
81        uint64_t        present         :1;
82        uint64_t        read_write      :1;
83        uint64_t        user_supervisor :1;
84        uint64_t        write_through   :1;
85        uint64_t        cache_disabled  :1;
86        uint64_t        accessed        :1;
87        uint64_t        dirty           :1;
88        uint64_t        always1         :1;
89        uint64_t        global          :1;
90        uint64_t        available       :3;
91        uint64_t        attr_index      :1;
92        uint64_t        reserved        :8;
93        uint64_t        base_addr       :19;
94        uint64_t        reserved2       :12;
95        uint64_t        available2      :11;
96        uint64_t        execute_disable :1;
97    } large;
98    struct {
99        uint64_t        present         :1;
100        uint64_t        read_write      :1;
101        uint64_t        user_supervisor :1;
102        uint64_t        write_through   :1;
103        uint64_t        cache_disabled  :1;
104        uint64_t        accessed        :1;
105        uint64_t        dirty           :1;
106        uint64_t        attr_index      :1;
107        uint64_t        global          :1;
108        uint64_t        available       :3;
109        uint64_t        base_addr       :28;
110        uint64_t        reserved2       :12;
111        uint64_t        available2      :11;
112        uint64_t        execute_disable :1;
113    } base;
114};
115
116struct multiboot_info *multiboot_info;
117uint32_t eax;
118
119union pdir_entry boot_pml4[PTABLE_SIZE]
120__attribute__ ((aligned(BASE_PAGE_SIZE)));
121
122static union pdir_entry pdpt[PTABLE_SIZE]
123__attribute__ ((aligned(BASE_PAGE_SIZE)));
124
125static union ptable_entry pdir[PTABLE_SIZE]
126__attribute__ ((aligned(BASE_PAGE_SIZE)));
127
128static lpaddr_t phys_alloc_start;
129
130static errval_t linear_alloc(void *s, genvaddr_t base, size_t size, uint32_t flags,
131                             void **ret)
132{
133    // round to base page size
134    uint32_t npages = (size + BASE_PAGE_SIZE - 1) / BASE_PAGE_SIZE;
135
136    /* *ret = (void *)(uintptr_t)base; */
137    *ret = (void *)phys_alloc_start;
138
139    phys_alloc_start += npages * BASE_PAGE_SIZE;
140    return SYS_ERR_OK;
141}
142
143static struct multiboot_modinfo *multiboot_find_module(const char *basename)
144{
145    struct multiboot_modinfo *mod = (struct multiboot_modinfo *)
146        multiboot_info->mods_addr;
147
148    for(size_t i = 0; i < multiboot_info->mods_count; i++) {
149        const char *modname = strrchr((char *)mod[i].string, '/');
150
151        if(modname == NULL) {
152            modname = (char *)mod[i].string;
153        } else {
154            modname++;
155        }
156
157        if(!strncmp(modname, basename, strlen(basename))) {
158            return &mod[i];
159        }
160    }
161
162    return NULL;
163}
164
165static uintptr_t multiboot_end_addr(void)
166{
167    lpaddr_t end = ((lpaddr_t)multiboot_info) + sizeof(struct multiboot_info);
168    struct multiboot_info *mi = multiboot_info;
169
170#define CHECK(pa)           { lpaddr_t tmp = pa; if (tmp > end) { end = tmp; } }
171#define CHECK_STR(pstr)     CHECK(pstr + strlen((char *)pstr) + 1)
172
173    if (mi->flags & MULTIBOOT_INFO_FLAG_HAS_CMDLINE) {
174        CHECK_STR(mi->cmdline)
175    }
176
177    if (mi->flags & MULTIBOOT_INFO_FLAG_HAS_MODS) {
178        struct multiboot_modinfo *mod = (void *)mi->mods_addr;
179
180        for(int i = 0; i < mi->mods_count; i++) {
181            CHECK(mod[i].mod_end)
182            CHECK_STR(mod[i].string)
183        }
184    }
185
186    if (mi->flags & MULTIBOOT_INFO_FLAG_HAS_ELF_SYMS) {
187        CHECK(mi->syms.elf.addr + mi->syms.elf.num * mi->syms.elf.size)
188        /* FIXME: does this include mi_elfshdr_shndx?? */
189    }
190
191    if (mi->flags & MULTIBOOT_INFO_FLAG_HAS_MMAP) {
192        CHECK(mi->mmap_addr + mi->mmap_length)
193    }
194
195    if (mi->flags & MULTIBOOT_INFO_FLAG_HAS_DRIVES) {
196        CHECK(mi->drives_addr + mi->drives_length)
197    }
198
199    if (mi->flags & MULTIBOOT_INFO_FLAG_HAS_LOADERNAME) {
200        CHECK_STR(mi->boot_loader_name)
201    }
202
203    /* TODO: config table, APM table, VBE */
204
205#undef CHECK
206#undef CHECK_STR
207
208    return end;
209}
210
211static inline void paging_map_table(union pdir_entry *entry, uint64_t base)
212{
213    entry->raw = PTABLE_CLEAR;
214
215    entry->d.present = 1;
216    entry->d.read_write = 1;
217    entry->d.user_supervisor = 1;
218    entry->d.base_addr = base >> 12;
219}
220
221static inline void paging_map_large(union ptable_entry *entry, uint64_t base,
222                                    uint64_t bitmap)
223{
224    entry->raw = PTABLE_CLEAR;
225
226    entry->large.present = bitmap & PTABLE_PRESENT ? 1 : 0;
227    entry->large.read_write = bitmap & PTABLE_READ_WRITE ? 1 : 0;
228    entry->large.user_supervisor = bitmap & PTABLE_USER_SUPERVISOR ? 1 : 0;
229    entry->large.write_through = bitmap & PTABLE_WRITE_THROUGH ? 1 : 0;
230    entry->large.cache_disabled = bitmap & PTABLE_CACHE_DISABLED ? 1 : 0;
231    entry->large.global = bitmap & PTABLE_GLOBAL_PAGE ? 1 : 0;
232    entry->large.attr_index = bitmap & PTABLE_ATTR_INDEX ? 1 : 0;
233    entry->large.execute_disable = bitmap & PTABLE_EXECUTE_DISABLE ? 1 : 0;
234    entry->large.always1 = 1;
235    entry->large.base_addr = base >> 21;
236}
237
238genvaddr_t kernel_entry;
239
240static void set_elf_headers(uint32_t base)
241{
242    struct Elf64_Ehdr *head = (struct Elf64_Ehdr *)base;
243
244    multiboot_info->syms.elf.num = head->e_shnum;
245    multiboot_info->syms.elf.size = head->e_shentsize;
246    multiboot_info->syms.elf.addr = base + head->e_shoff;
247    multiboot_info->syms.elf.shndx = head->e_shstrndx;
248    multiboot_info->flags |= MULTIBOOT_INFO_FLAG_HAS_ELF_SYMS;
249}
250
251int startup(uint32_t magic, struct multiboot_info *mb);
252
253int startup(uint32_t magic, struct multiboot_info *mb)
254{
255    errval_t err;
256
257    // Store important registers
258    multiboot_info = mb;
259    eax = magic;
260
261    // Look for the kernel to boot, which may have several names
262    struct multiboot_modinfo *kernel;
263    kernel = multiboot_find_module("cpu");
264    if (kernel == NULL) {
265        kernel = multiboot_find_module("kernel");
266    }
267
268    // Reserve a page before kernel start
269    phys_alloc_start = ROUND_UP(multiboot_end_addr(), BASE_PAGE_SIZE) +
270        BASE_PAGE_SIZE;
271    lpaddr_t kernel_start = phys_alloc_start;
272
273    err = elf64_load(EM_X86_64, linear_alloc, NULL, kernel->mod_start,
274                     MULTIBOOT_MODULE_SIZE(*kernel), &kernel_entry, NULL, NULL, NULL);
275    if (err_is_fail(err)) {
276        printf("Elver ELF loading failed!\n");
277        return -1;
278    }
279
280    // Relocate kernel image
281    struct Elf64_Ehdr *cpu_head = (struct Elf64_Ehdr *)kernel->mod_start;
282    struct Elf64_Shdr *rela, *symtab, *symhead =
283        (struct Elf64_Shdr *)(kernel->mod_start + (uintptr_t)cpu_head->e_shoff);
284    genvaddr_t elfbase = elf_virtual_base64(cpu_head);
285    rela = elf64_find_section_header_type(symhead, cpu_head->e_shnum, SHT_RELA);
286    symtab = elf64_find_section_header_type(symhead, cpu_head->e_shnum, SHT_DYNSYM);
287    elf64_relocate(kernel_start, elfbase,
288                   (struct Elf64_Rela *)(uintptr_t)(kernel->mod_start + rela->sh_offset),
289                   rela->sh_size,
290                   (struct Elf64_Sym *)(uintptr_t)(kernel->mod_start + symtab->sh_offset),
291                   symtab->sh_size,
292                   elfbase, (void *)kernel_start);
293    kernel_entry = kernel_entry - elfbase + kernel_start;
294
295    // Identity map the first 1 GByte of physical memory in long mode
296    paging_map_table(&boot_pml4[PML4_BASE(0)], (uint64_t)(uint32_t)pdpt);
297    paging_map_table(&pdpt[PDPT_BASE(0)], (uint64_t)(uint32_t)pdir);
298    for(uint32_t i = 0; i < ONE_GIB; i += LARGE_PAGE_SIZE) {
299        paging_map_large(&pdir[PDIR_BASE(i)], i, PTABLE_PRESENT
300                         | PTABLE_READ_WRITE | PTABLE_USER_SUPERVISOR);
301    }
302
303    // Put real kernel's ELF symbols into multiboot
304    set_elf_headers(kernel->mod_start);
305
306    return 0;
307}
308