1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "trampoline.h" 6 7#include <inttypes.h> 8#include <libzbi/zbi.h> 9#include <string.h> 10#include <zircon/boot/e820.h> 11 12#define BOOT_LOADER_NAME_ENV "multiboot.boot_loader_name=" 13 14static size_t zbi_size(const zbi_header_t* zbi) { 15 return sizeof(*zbi) + zbi->length; 16} 17 18// Convert the multiboot memory information to ZBI_TYPE_E820_TABLE format. 19static void add_memory_info(void* zbi, size_t capacity, 20 const multiboot_info_t* info) { 21 if ((info->flags & MB_INFO_MMAP) && 22 info->mmap_addr != 0 && 23 info->mmap_length >= sizeof(memory_map_t)) { 24 size_t nranges = 0; 25 for (memory_map_t* mmap = (void*)info->mmap_addr; 26 (uintptr_t)mmap < info->mmap_addr + info->mmap_length; 27 mmap = (void*)((uintptr_t)mmap + 4 + mmap->size)) { 28 ++nranges; 29 } 30 e820entry_t* ranges; 31 void* payload; 32 zbi_result_t result = zbi_create_section( 33 zbi, capacity, nranges * sizeof(ranges[0]), 34 ZBI_TYPE_E820_TABLE, 0, 0, &payload); 35 if (result != ZBI_RESULT_OK) { 36 panic("zbi_create_section(%p, %#"PRIxPTR", %#zx) failed: %d", 37 zbi, capacity, nranges * sizeof(ranges[0]), (int)result); 38 } 39 ranges = payload; 40 for (memory_map_t* mmap = (void*)info->mmap_addr; 41 (uintptr_t)mmap < info->mmap_addr + info->mmap_length; 42 mmap = (void*)((uintptr_t)mmap + 4 + mmap->size)) { 43 *ranges++ = (e820entry_t){ 44 .addr = (((uint64_t)mmap->base_addr_high << 32) | 45 mmap->base_addr_low), 46 .size = (((uint64_t)mmap->length_high << 32) | 47 mmap->length_low), 48 // MB_MMAP_TYPE_* matches E820_* values. 49 .type = mmap->type, 50 }; 51 } 52 } else { 53 const e820entry_t ranges[] = { 54 { 55 .addr = 0, 56 .size = info->mem_lower << 10, 57 .type = E820_RAM, 58 }, 59 { 60 .addr = 1 << 20, 61 .size = ((uint64_t)info->mem_upper << 10) - (1 << 20), 62 .type = E820_RAM, 63 }, 64 }; 65 zbi_result_t result = zbi_append_section( 66 zbi, capacity, sizeof(ranges), ZBI_TYPE_E820_TABLE, 0, 0, ranges); 67 if (result != ZBI_RESULT_OK) { 68 panic("zbi_append_section(%p, %#"PRIxPTR", %#zx) failed: %d", 69 zbi, capacity, sizeof(ranges), (int)result); 70 } 71 } 72} 73 74static void add_cmdline(void* zbi, size_t capacity, 75 const multiboot_info_t* info) { 76 // Boot loader command line. 77 if (info->flags & MB_INFO_CMD_LINE) { 78 const char* cmdline = (void*)info->cmdline; 79 size_t len = strlen(cmdline) + 1; 80 zbi_result_t result = zbi_append_section( 81 zbi, capacity, len, ZBI_TYPE_CMDLINE, 0, 0, cmdline); 82 if (result != ZBI_RESULT_OK) { 83 panic("zbi_append_section(%p, %#"PRIxPTR", %zu) failed: %d", 84 zbi, capacity, len, (int)result); 85 } 86 } 87 88 // Boot loader name. 89 if (info->flags & MB_INFO_BOOT_LOADER) { 90 const char* name = (void*)info->boot_loader_name; 91 size_t len = strlen(name) + 1; 92 void *payload; 93 zbi_result_t result = zbi_create_section( 94 zbi, capacity, sizeof(BOOT_LOADER_NAME_ENV) - 1 + len, 95 ZBI_TYPE_CMDLINE, 0, 0, &payload); 96 if (result != ZBI_RESULT_OK) { 97 panic("zbi_create_section(%p, %#"PRIxPTR", %zu) failed: %d", 98 zbi, capacity, sizeof(BOOT_LOADER_NAME_ENV) - 1 + len, 99 (int)result); 100 } 101 for (char *p = (memcpy(payload, BOOT_LOADER_NAME_ENV, 102 sizeof(BOOT_LOADER_NAME_ENV) - 1) + 103 sizeof(BOOT_LOADER_NAME_ENV) - 1); 104 len > 0; 105 ++p, ++name, --len) { 106 *p = (*name == ' ' || *name == '\t' || 107 *name == '\n' || *name == '\r') ? '+' : *name; 108 } 109 } 110} 111 112static void add_zbi_items(void* zbi, size_t capacity, 113 const multiboot_info_t* info) { 114 add_memory_info(zbi, capacity, info); 115 add_cmdline(zbi, capacity, info); 116} 117 118noreturn void multiboot_main(uint32_t magic, multiboot_info_t* info) { 119 if (magic != MULTIBOOT_BOOTLOADER_MAGIC) { 120 panic("bad multiboot magic from bootloader %#"PRIx32" != %#"PRIx32"\n", 121 magic, (uint32_t)MULTIBOOT_BOOTLOADER_MAGIC); 122 } 123 124 if (!(info->flags & MB_INFO_MODS)) { 125 panic("missing multiboot modules"); 126 } 127 if (info->mods_count != 1) { 128 panic("cannot handle multiboot mods_count %"PRIu32" != 1\n", 129 info->mods_count); 130 } 131 132 module_t* const mod = (void*)info->mods_addr; 133 zircon_kernel_t* zbi = (void*)mod->mod_start; 134 size_t zbi_len = mod->mod_end - mod->mod_start; 135 136 if (zbi == NULL || zbi_len < sizeof(zbi->hdr_file) || 137 zbi_len < sizeof(zbi_header_t) + zbi->hdr_file.length) { 138 panic("insufficient multiboot module [%#"PRIx32",%#"PRIx32") for ZBI", 139 mod->mod_start, mod->mod_end); 140 } 141 142 zbi_header_t* bad_hdr; 143 zbi_result_t result = zbi_check(zbi, &bad_hdr); 144 if (result != ZBI_RESULT_OK) { 145 panic("ZBI failed check: %d at offset %#zx", 146 (int)result, (size_t)((uint8_t*)bad_hdr - (uint8_t*)zbi)); 147 } 148 149 if (zbi->hdr_kernel.type != ZBI_TYPE_KERNEL_X64) { 150 panic("ZBI first item has type %#"PRIx32" != %#"PRIx32"\n", 151 zbi->hdr_kernel.type, (uint32_t)ZBI_TYPE_KERNEL_X64); 152 } 153 154 // The kernel will sit at PHYS_LOAD_ADDRESS, where the code now 155 // running sits. The space until kernel_memory_end is reserved 156 // and can't be used for anything else. 157 const size_t kernel_load_size = 158 offsetof(zircon_kernel_t, data_kernel) + zbi->hdr_kernel.length; 159 uint8_t* const kernel_load_end = PHYS_LOAD_ADDRESS + kernel_load_size; 160 uint8_t* const kernel_memory_end = 161 kernel_load_end + ZBI_ALIGN(zbi->data_kernel.reserve_memory_size); 162 163 if (!(info->flags & MB_INFO_MEM_SIZE)) { 164 panic("multiboot memory information missing"); 165 } 166 uintptr_t upper_memory_limit = info->mem_upper << 10; 167 if (info->mem_upper > (UINT32_MAX >> 10)) { 168 upper_memory_limit = -4096u; 169 } 170 if (upper_memory_limit < (uintptr_t)kernel_memory_end) { 171 panic("upper memory limit %#"PRIxPTR" < kernel end %p", 172 upper_memory_limit, kernel_memory_end); 173 } 174 175 // Now we can append other items to the ZBI. 176 const size_t capacity = upper_memory_limit - (uintptr_t)zbi; 177 add_zbi_items(zbi, capacity, info); 178 179 // Use discarded ZBI space to hold the trampoline. 180 void* trampoline; 181 result = zbi_create_section(zbi, capacity, sizeof(struct trampoline), 182 ZBI_TYPE_DISCARD, 0, 0, &trampoline); 183 if (result != ZBI_RESULT_OK) { 184 panic("zbi_create_section(%p, %#"PRIxPTR", %#zx) failed: %d", 185 zbi, capacity, sizeof(struct trampoline), (int)result); 186 } 187 188 // Separate the kernel from the data ZBI and move it into temporary 189 // space that the trampoline code will copy it from. 190 const zbi_header_t data_zbi_hdr = ZBI_CONTAINER_HEADER( 191 zbi->hdr_file.length - 192 sizeof(zbi->hdr_kernel) - 193 zbi->hdr_kernel.length); 194 uint8_t* const zbi_end = (uint8_t*)zbi + zbi_size(&zbi->hdr_file); 195 zircon_kernel_t* kernel; 196 zbi_header_t* data; 197 uintptr_t free_memory; 198 if ((uint8_t*)zbi < kernel_memory_end) { 199 // The ZBI overlaps where the kernel needs to sit. Copy it further up. 200 if (zbi_end < kernel_memory_end) { 201 data = (void*)kernel_memory_end; 202 } else { 203 data = (void*)zbi_end; 204 } 205 // It needs to be page-aligned. 206 data = (void*)(((uintptr_t)data + 4095) & -4096); 207 // First fill in the data ZBI. 208 *data = data_zbi_hdr; 209 memcpy(data + 1, 210 (uint8_t*)&zbi->data_kernel + zbi->hdr_kernel.length, 211 data->length); 212 // Now place the kernel after that. 213 kernel = (void*)((uint8_t*)(data + 1) + data->length); 214 memcpy(kernel, zbi, kernel_load_size); 215 free_memory = (uintptr_t)kernel + kernel_load_size; 216 } else { 217 // The ZBI can stay where it is. Find a place to copy the kernel. 218 size_t space = (uint8_t*)zbi - kernel_memory_end; 219 if (space >= kernel_load_size) { 220 // There's space right after the final kernel location. 221 kernel = (void*)kernel_memory_end; 222 free_memory = (uintptr_t)zbi_end; 223 } else { 224 // Use space after the ZBI. 225 kernel = (void*)((uint8_t*)zbi + zbi_size(&zbi->hdr_file)); 226 free_memory = (uintptr_t)kernel + kernel_load_size; 227 } 228 // Copy the kernel into some available scratch space. 229 memcpy(kernel, zbi, kernel_load_size); 230 // Now clobber what was the tail end of the kernel with a new 231 // container header for just the remainder of the ZBI. 232 data = (void*)((uint8_t*)&zbi->data_kernel + zbi->hdr_kernel.length); 233 *data = data_zbi_hdr; 234 // Move the ZBI up to get it page-aligned. 235 if ((uintptr_t)data % 4096 != 0) { 236 void* orig = data; 237 data = (void*)(((uintptr_t)data + 4095) & -4096); 238 memmove(data, orig, zbi_size(data)); 239 } 240 } 241 242 // Fix up the kernel container's size. 243 kernel->hdr_file.length = 244 sizeof(kernel->hdr_kernel) + kernel->hdr_kernel.length; 245 246 // Set up page tables in free memory. 247 enable_64bit_paging(free_memory, upper_memory_limit); 248 249 // Copy the kernel into place and enter its code in 64-bit mode. 250 boot_zbi(kernel, data, trampoline); 251} 252