Patches b03c1b through 3f00b5
[barrelfish] / tools / weever / loader.c
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, 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
29 #include "mbi.h"
30 #include "kernel_boot_param.h"
31
32 #include "../../kernel/include/multiboot.h"
33
34 /* the boot magic */
35 #define K1OM_BOOT_MAGIC         0xB001B001
36
37 /* the address of the Xeon Phi SBOX registers used for status prints*/
38 #define SBOX_BASE           0x08007D0000ULL
39
40 /* reference to the end of bootloader */
41 extern char _end_bootloader;
42
43 /// Round up n to the next multiple of size
44 #define ROUND_UP(n, size)           ((((n) + (size) - 1)) & (~((size) - 1)))
45 #define BASE_PAGE_SIZE 0x1000
46
47 /* Pointer to the multiboot struct we use */
48 struct multiboot_info *multiboot_info;
49
50 /* Address where we can safely allocate memory */
51 static lpaddr_t phys_alloc_start;
52
53 /* the entry address of the loaded kernel */
54 genvaddr_t kernel_entry;
55
56 /**
57  * C level entry point for the boot loader
58  *
59  * \param magic this field must hold the value K1OM_BOOT_MAGIC
60  * \param mb    pointer to the boot_params struct setup by the boot loader
61  */
62 int
63 loader(uint64_t magic,
64        struct boot_params *mb);
65
66 /*
67  * ----------------------------------------------------------------------------
68  *  Basic Error Reporting Mechanism
69  * ----------------------------------------------------------------------------
70  */
71 union status
72 {
73     uint32_t raw;
74     char vals[4];
75 };
76
77 static void
78 print_status(char a,
79              char b)
80 {
81
82     volatile uint32_t *p = (volatile uint32_t *) ((SBOX_BASE) + 0x0000AB40);
83     volatile uint32_t *p2 = (volatile uint32_t *) ((SBOX_BASE) + 0x0000AB5C);
84
85     union status s;
86
87     s.vals[3] = 0x0a;
88     s.vals[2] = b;
89     s.vals[1] = a;
90     s.vals[0] = '>';
91
92     *p2 = s.raw;
93     *p = 0x7A7A7A7A;
94 }
95
96 static inline void
97 eabort(char a,
98        char b)
99 {
100     print_status(a, b);
101     while (1)
102         ;
103 }
104
105 static inline void notify_host(void)
106 {
107     volatile uint32_t *p = (volatile uint32_t *) ((SBOX_BASE) + 0xAB28);
108     *p = (*p) | 0x1;
109 }
110
111
112 /*
113  * ----------------------------------------------------------------------------
114  *  ELF Utility Functions
115  * ----------------------------------------------------------------------------
116  */
117
118 static errval_t
119 linear_alloc(void *s,
120              genvaddr_t base,
121              size_t size,
122              uint32_t flags,
123              void **ret)
124 {
125     // round to base page size
126     uint32_t npages = (size + BASE_PAGE_SIZE - 1) / BASE_PAGE_SIZE;
127
128     *ret = (void *) phys_alloc_start;
129
130     phys_alloc_start += npages * BASE_PAGE_SIZE;
131     return SYS_ERR_OK;
132 }
133
134
135 static struct multiboot_modinfo *
136 multiboot_find_module(const char *basename)
137 {
138     struct multiboot_modinfo *mod;
139     mod = (struct multiboot_modinfo *) (uintptr_t) multiboot_info->mods_addr;
140
141     for (size_t i = 0; i < multiboot_info->mods_count; i++) {
142         const char *modname = strrchr((char *) (uintptr_t) mod[i].string, '/');
143
144         if (modname == NULL) {
145             modname = (char *) (uintptr_t) mod[i].string;
146         } else {
147             modname++;
148         }
149
150         if (!strncmp(modname, basename, strlen(basename))) {
151             return &mod[i];
152         }
153     }
154
155     return NULL;
156 }
157
158 static void
159 set_elf_headers(uintptr_t base)
160 {
161     struct Elf64_Ehdr *head = (struct Elf64_Ehdr *) (base);
162
163     multiboot_info->syms.elf.num = head->e_shnum;
164     multiboot_info->syms.elf.size = head->e_shentsize;
165     multiboot_info->syms.elf.addr = base + head->e_shoff;
166     multiboot_info->syms.elf.shndx = head->e_shstrndx;
167 }
168
169 /*
170  * ----------------------------------------------------------------------------
171  *  Loader
172  * ----------------------------------------------------------------------------
173  */
174
175 /**
176  * Entry point from boot.S
177  * Long mode, paging and protected mode enabled
178  *
179  * \param magic         magic value
180  * \param bootparam     pointer to struct boot param
181  *
182  */
183 int
184 loader(uint64_t magic,
185        struct boot_params *bootparam)
186 {
187     errval_t err;
188
189     print_status('S', '0');
190
191     if (magic != K1OM_BOOT_MAGIC) {
192         /* wrong value */
193         eabort('E', '0');
194     }
195
196     struct boot_params *bp = (struct boot_params *) bootparam;
197     struct setup_header *boot_hdr = (struct setup_header *) &bp->hdr;
198
199     multiboot_info = get_multiboot();
200
201     print_status('S', '1');
202
203     /*
204      * Copy the multi boot image closer to the kernel
205      */
206     lpaddr_t mb_img_start = ROUND_UP((lpaddr_t )&_end_bootloader, 1 << 21);
207     lpaddr_t mb_img_orig = boot_hdr->ramdisk_image;
208
209     /* sanity check for the locations */
210     if (mb_img_start > mb_img_orig || mb_img_start == 0) {
211         eabort('E', '1');
212     }
213     memcpy((void *) mb_img_start, (void *) mb_img_orig, boot_hdr->ramdisk_size);
214
215     /*
216      * the multiboot does only stores the offsets within the multiboot image
217      * thus we have to adjust the addresses in the multiboot info struct
218      */
219     struct multiboot_modinfo *mod;
220     mod = (struct multiboot_modinfo *) (uintptr_t) multiboot_info->mods_addr;
221
222     for (size_t i = 0; i < multiboot_info->mods_count; i++) {
223         mod->mod_start += mb_img_start;
224         mod->mod_end += mb_img_start;
225         mod++;
226     }
227     print_status('S', '2');
228
229     /* look up the kernel module */
230     struct multiboot_modinfo *kernel;
231     kernel = multiboot_find_module("cpu");
232     if (kernel == NULL) {
233         kernel = multiboot_find_module("kernel");
234     }
235     if (kernel == NULL) {
236         eabort('E', '2');
237     }
238
239     /* set the start address where we can allocate ram */
240     phys_alloc_start = ROUND_UP(mb_img_start + boot_hdr->ramdisk_size,
241                                 BASE_PAGE_SIZE)+BASE_PAGE_SIZE;
242
243     boot_hdr->ramdisk_image = (uint32_t) mb_img_start;
244
245     lpaddr_t kernel_start = phys_alloc_start;
246
247     /* overwrite the cmd line with the one supplied by the host */
248     multiboot_info->cmdline = boot_hdr->cmd_line_ptr;
249
250     /* we use the mem_lower and mem_upper for the mulitboot image location */
251
252     multiboot_info->mem_lower = boot_hdr->ramdisk_image;
253     multiboot_info->mem_upper = boot_hdr->ramdisk_image+boot_hdr->ramdisk_size;
254     multiboot_info->flags |= MULTIBOOT_INFO_FLAG_HAS_MEMINFO;
255
256     /* we use the config table to store the pointer to struct boot param */
257     multiboot_info->config_table = (uint32_t)(uintptr_t)bootparam;
258     multiboot_info->flags |= MULTIBOOT_INFO_FLAG_HAS_CONFIG;
259
260     boot_hdr->multiboot = (uint64_t)multiboot_info;
261
262     print_status('S', '3');
263
264     err = elf64_load(EM_K1OM, linear_alloc, NULL, kernel->mod_start,
265                      MULTIBOOT_MODULE_SIZE(*kernel), &kernel_entry, NULL, NULL,
266                      NULL);
267
268     if (err_is_fail(err)) {
269         eabort('E', '3');
270     }
271
272     struct Elf64_Ehdr *cpu_head = (struct Elf64_Ehdr *) (uint64_t) kernel->mod_start;
273     struct Elf64_Shdr *rela, *symtab, *symhead;
274
275     symhead = (struct Elf64_Shdr *) (kernel->mod_start
276             + (uintptr_t) cpu_head->e_shoff);
277
278     genvaddr_t elfbase = elf_virtual_base64(cpu_head);
279
280     rela = elf64_find_section_header_type(symhead, cpu_head->e_shnum, SHT_RELA);
281
282     symtab = elf64_find_section_header_type(symhead, cpu_head->e_shnum, SHT_DYNSYM);
283
284     print_status('S', '4');
285
286     elf64_relocate(
287             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, elfbase, (void *) kernel_start);
292
293     kernel_entry = kernel_entry - elfbase + kernel_start;
294
295     print_status('S', '5');
296
297     set_elf_headers(kernel->mod_start);
298
299     print_status('S', '6');
300
301     return kernel_entry;
302
303 }
304