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