1/* 2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10 11#include <elf/elf.h> 12#include <sel4utils/mapping.h> 13#include <vka/capops.h> 14#include <cpio/cpio.h> 15 16#include <sel4vmmplatsupport/guest_image.h> 17 18#include <sel4vm/guest_vm.h> 19#include <sel4vm/guest_memory.h> 20#include <sel4vm/guest_ram.h> 21 22typedef struct boot_guest_cookie { 23 vm_t *vm; 24 FILE *file; 25} boot_guest_cookie_t; 26 27/* Reads the elf header and elf program headers from a file when given a sufficiently 28 * large memory buffer 29 */ 30static int read_elf_headers(void *buf, vm_t *vm, FILE *file, size_t buf_size, elf_t *elf) 31{ 32 size_t result; 33 if (buf_size < sizeof(Elf32_Ehdr)) { 34 return -1; 35 } 36 fseek(file, 0, SEEK_SET); 37 result = fread(buf, buf_size, 1, file); 38 if (result != 1) { 39 return -1; 40 } 41 42 return elf_newFile_maybe_unsafe(buf, buf_size, true, false, elf); 43} 44 45static int guest_elf_write_address(vm_t *vm, uintptr_t paddr, void *vaddr, size_t size, size_t offset, void *cookie) 46{ 47 memcpy(vaddr, cookie + offset, size); 48 return 0; 49} 50 51static int guest_elf_read_address(vm_t *vm, uintptr_t paddr, void *vaddr, size_t size, size_t offset, void *cookie) 52{ 53 memcpy(cookie + offset, vaddr, size); 54 return 0; 55} 56 57int guest_elf_relocate(vm_t *vm, const char *relocs_filename, guest_kernel_image_t *image) 58{ 59 int delta = image->kernel_image_arch.relocation_offset; 60 if (delta == 0) { 61 /* No relocation needed. */ 62 return 0; 63 } 64 65 uintptr_t load_addr = image->kernel_image_arch.link_paddr; 66 ZF_LOGI("plat: relocating guest kernel from 0x%x --> 0x%x", (unsigned int)load_addr, 67 (unsigned int)(load_addr + delta)); 68 69 /* Open the relocs file. */ 70 ZF_LOGI("plat: opening relocs file %s", relocs_filename); 71 72 size_t relocs_size = 0; 73 FILE *file = fopen(relocs_filename, "r"); 74 if (!file) { 75 ZF_LOGE("ERROR: Guest OS kernel relocation is required, but corresponding" 76 "%s was not found. This is most likely due to a Makefile" 77 "error, or configuration error.\n", relocs_filename); 78 return -1; 79 } 80 fseek(file, 0, SEEK_END); 81 relocs_size = ftell(file); 82 fseek(file, 0, SEEK_SET); 83 84 /* The relocs file is the same relocs file format used by the Linux kernel decompressor to 85 * relocate the Linux kernel: 86 * 87 * 0 - zero terminator for 64 bit relocations 88 * 64 bit relocation repeated 89 * 0 - zero terminator for 32 bit relocations 90 * 32 bit relocation repeated 91 * <EOF> 92 * 93 * So we work backwards from the end of the file, and modify the guest kernel OS binary. 94 * We only support 32-bit relocations, and ignore the 64-bit data. 95 * 96 * src: Linux kernel 3.5.3 arch/x86/boot/compressed/misc.c 97 */ 98 uint32_t last_relocated_vaddr = 0xFFFFFFFF; 99 uint32_t num_relocations = relocs_size / sizeof(uint32_t) - 1; 100 for (int i = 0; ; i++) { 101 uint32_t vaddr; 102 /* Get the next relocation from the relocs file. */ 103 uint32_t offset = relocs_size - (sizeof(uint32_t) * (i + 1)); 104 fseek(file, offset, SEEK_SET); 105 size_t result = fread(&vaddr, sizeof(uint32_t), 1, file); 106 ZF_LOGF_IF(result != 1, "Read failed unexpectedly"); 107 if (!vaddr) { 108 break; 109 } 110 assert(i * sizeof(uint32_t) < relocs_size); 111 last_relocated_vaddr = vaddr; 112 113 /* Calculate the corresponding guest-physical address at which we have already 114 allocated and mapped the ELF contents into. */ 115 assert(vaddr >= (uint32_t)image->kernel_image_arch.link_vaddr); 116 uintptr_t guest_paddr = (uintptr_t)vaddr - (uintptr_t)image->kernel_image_arch.link_vaddr + 117 (uintptr_t)(load_addr + delta); 118 119 /* Perform the relocation. */ 120 ZF_LOGI(" reloc vaddr 0x%x guest_addr 0x%x", (unsigned int)vaddr, (unsigned int)guest_paddr); 121 uint32_t addr; 122 vm_ram_touch(vm, guest_paddr, sizeof(int), 123 guest_elf_read_address, &addr); 124 addr += delta; 125 vm_ram_touch(vm, guest_paddr, sizeof(int), 126 guest_elf_write_address, &addr); 127 128 if (i && i % 50000 == 0) { 129 ZF_LOGE(" %u relocs done.", i); 130 } 131 } 132 ZF_LOGI("plat: last relocated addr was %d", last_relocated_vaddr); 133 ZF_LOGI("plat: %d kernel relocations completed.", num_relocations); 134 (void) last_relocated_vaddr; 135 136 if (num_relocations == 0) { 137 ZF_LOGE("Relocation required, but Kernel has not been build with CONFIG_RELOCATABLE."); 138 return -1; 139 } 140 141 fclose(file); 142 return 0; 143} 144 145/* TODO: Refactor and stop rewriting fucking elf loading code */ 146static int load_guest_segment(vm_t *vm, seL4_Word source_offset, 147 seL4_Word dest_addr, unsigned int segment_size, unsigned int file_size, FILE *file) 148{ 149 150 int ret; 151 unsigned int page_size = seL4_PageBits; 152 assert(file_size <= segment_size); 153 154 /* Allocate a cslot for duplicating frame caps */ 155 cspacepath_t dup_slot; 156 ret = vka_cspace_alloc_path(vm->vka, &dup_slot); 157 if (ret) { 158 ZF_LOGE("Failed to allocate slot"); 159 return ret; 160 } 161 162 size_t current = 0; 163 size_t remain = file_size; 164 while (current < segment_size) { 165 /* Retrieve the mapping */ 166 seL4_CPtr cap; 167 cap = vspace_get_cap(&vm->mem.vm_vspace, (void *)dest_addr); 168 if (!cap) { 169 ZF_LOGE("Failed to find frame cap while loading elf segment at %p", (void *)dest_addr); 170 return -1; 171 } 172 cspacepath_t cap_path; 173 vka_cspace_make_path(vm->vka, cap, &cap_path); 174 175 /* Copy cap and map into our vspace */ 176 vka_cnode_copy(&dup_slot, &cap_path, seL4_AllRights); 177 void *map_vaddr = vspace_map_pages(&vm->mem.vmm_vspace, &dup_slot.capPtr, NULL, seL4_AllRights, 1, page_size, 1); 178 if (!map_vaddr) { 179 ZF_LOGE("Failed to map page into host vspace"); 180 return -1; 181 } 182 183 /* Copy the contents of page from ELF into the mapped frame. */ 184 size_t offset = dest_addr & ((BIT(page_size)) - 1); 185 186 void *copy_vaddr = map_vaddr + offset; 187 size_t copy_len = (BIT(page_size)) - offset; 188 189 if (remain > 0) { 190 if (copy_len > remain) { 191 /* Don't copy past end of data. */ 192 copy_len = remain; 193 } 194 195 ZF_LOGI("load page src %zu dest %p remain %zu offset %zu copy vaddr %p " 196 "copy len %zu\n", source_offset, (void *)dest_addr, remain, offset, copy_vaddr, copy_len); 197 198 fseek(file, source_offset, SEEK_SET); 199 size_t result = fread(copy_vaddr, copy_len, 1, file); 200 ZF_LOGF_IF(result != 1, "Read failed unexpectedly"); 201 source_offset += copy_len; 202 remain -= copy_len; 203 } else { 204 memset(copy_vaddr, 0, copy_len); 205 } 206 207 dest_addr += copy_len; 208 209 current += copy_len; 210 211 /* Unamp the page and delete the temporary cap */ 212 vspace_unmap_pages(&vm->mem.vmm_vspace, map_vaddr, 1, page_size, NULL); 213 vka_cnode_delete(&dup_slot); 214 } 215 216 return 0; 217} 218 219static int load_guest_elf(vm_t *vm, const char *image_name, uintptr_t load_address, size_t alignment, 220 guest_kernel_image_t *guest_image) 221{ 222 elf_t kernel_elf; 223 char elf_file[256]; 224 int ret; 225 FILE *file = fopen(image_name, "r"); 226 if (!file) { 227 ZF_LOGE("Guest kernel elf \"%s\" not found.", image_name); 228 return -1; 229 } 230 231 ret = read_elf_headers(elf_file, vm, file, sizeof(elf_file), &kernel_elf); 232 if (ret < 0) { 233 ZF_LOGE("Guest elf \"%s\" invalid.", image_name); 234 return -1; 235 } 236 237 unsigned int n_headers = elf_getNumProgramHeaders(&kernel_elf); 238 239 /* Round up by the alignment. We just hope its still in the memory region. 240 * if it isn't we will just fail when we try and get the frame */ 241 uintptr_t load_addr = ROUND_UP(load_address, alignment); 242 /* Calculate relocation offset. */ 243 uintptr_t guest_kernel_addr = 0xFFFFFFFF; 244 uintptr_t guest_kernel_vaddr = 0xFFFFFFFF; 245 for (int i = 0; i < n_headers; i++) { 246 if (elf_getProgramHeaderType(&kernel_elf, i) != PT_LOAD) { 247 continue; 248 } 249 uint32_t addr = elf_getProgramHeaderPaddr(&kernel_elf, i); 250 if (addr < guest_kernel_addr) { 251 guest_kernel_addr = addr; 252 } 253 uint32_t vaddr = elf_getProgramHeaderVaddr(&kernel_elf, i); 254 if (vaddr < guest_kernel_vaddr) { 255 guest_kernel_vaddr = vaddr; 256 } 257 } 258 259 int guest_relocation_offset = (int)((int64_t)load_addr - (int64_t)guest_kernel_addr); 260 261 ZF_LOGI("Guest kernel is compiled to be located at paddr 0x%x vaddr 0x%x", 262 (unsigned int)guest_kernel_addr, (unsigned int)guest_kernel_vaddr); 263 ZF_LOGI("Guest kernel allocated 1:1 start is at paddr = 0x%x", (unsigned int)load_addr); 264 ZF_LOGI("Therefore relocation offset is %d (%s0x%x)", 265 guest_relocation_offset, 266 guest_relocation_offset < 0 ? "-" : "", 267 abs(guest_relocation_offset)); 268 269 for (int i = 0; i < n_headers; i++) { 270 seL4_Word source_offset, dest_addr; 271 unsigned int file_size, segment_size; 272 273 /* Skip unloadable program headers. */ 274 if (elf_getProgramHeaderType(&kernel_elf, i) != PT_LOAD) { 275 continue; 276 } 277 278 /* Fetch information about this segment. */ 279 source_offset = elf_getProgramHeaderOffset(&kernel_elf, i); 280 file_size = elf_getProgramHeaderFileSize(&kernel_elf, i); 281 segment_size = elf_getProgramHeaderMemorySize(&kernel_elf, i); 282 283 dest_addr = elf_getProgramHeaderPaddr(&kernel_elf, i); 284 dest_addr += guest_relocation_offset; 285 286 if (!segment_size) { 287 /* Zero sized segment, ignore. */ 288 continue; 289 } 290 291 /* Load this ELf segment. */ 292 ret = load_guest_segment(vm, source_offset, dest_addr, segment_size, file_size, file); 293 if (ret) { 294 return ret; 295 } 296 297 /* Record it as allocated */ 298 vm_ram_mark_allocated(vm, dest_addr, segment_size); 299 } 300 301 /* Record the entry point. */ 302 guest_image->kernel_image_arch.entry = elf_getEntryPoint(&kernel_elf); 303 guest_image->kernel_image_arch.entry += guest_relocation_offset; 304 305 /* Remember where we started loading the kernel to fix up relocations in future */ 306 guest_image->kernel_image.load_paddr = load_addr; 307 guest_image->kernel_image_arch.link_paddr = guest_kernel_addr; 308 guest_image->kernel_image_arch.link_vaddr = guest_kernel_vaddr; 309 guest_image->kernel_image_arch.relocation_offset = guest_relocation_offset; 310 guest_image->kernel_image.alignment = alignment; 311 312 fclose(file); 313 314 return 0; 315} 316 317int vm_load_guest_kernel(vm_t *vm, const char *kernel_name, uintptr_t load_address, size_t alignment, 318 guest_kernel_image_t *guest_kernel_image) 319{ 320 int err; 321 err = load_guest_elf(vm, kernel_name, load_address, alignment, guest_kernel_image); 322 if (err) { 323 ZF_LOGE("Failed to load guest elf"); 324 return err; 325 } 326 if (guest_kernel_image->kernel_image_arch.is_reloc_enabled) { 327 err = guest_elf_relocate(vm, guest_kernel_image->kernel_image_arch.relocs_file, guest_kernel_image); 328 if (err) { 329 ZF_LOGE("Failed to relocation guest kernel elf"); 330 } 331 } 332 return err; 333} 334 335static int load_module_continued(vm_t *vm, uintptr_t paddr, void *addr, size_t size, size_t offset, void *cookie) 336{ 337 boot_guest_cookie_t *pass = (boot_guest_cookie_t *) cookie; 338 fseek(pass->file, offset, SEEK_SET); 339 size_t result = fread(addr, size, 1, pass->file); 340 ZF_LOGF_IF(result != 1, "Read failed unexpectedly"); 341 342 return 0; 343} 344 345int vm_load_guest_module(vm_t *vm, const char *module_name, uintptr_t load_address, size_t alignment, 346 guest_image_t *guest_image) 347{ 348 ZF_LOGI("Loading module \"%s\" at 0x%x\n", module_name, (unsigned int)load_address); 349 350 size_t module_size = 0; 351 FILE *file = fopen(module_name, "r"); 352 if (!file) { 353 ZF_LOGE("Module \"%s\" not found.", module_name); 354 return -1; 355 } 356 fseek(file, 0, SEEK_END); 357 module_size = ftell(file); 358 fseek(file, 0, SEEK_SET); 359 if (!module_size) { 360 ZF_LOGE("Module has zero size. This is probably not what you want."); 361 return -1; 362 } 363 364 vm_ram_mark_allocated(vm, load_address, module_size); 365 boot_guest_cookie_t pass = { .vm = vm, .file = file}; 366 vm_ram_touch(vm, load_address, module_size, load_module_continued, &pass); 367 368 fclose(file); 369 370 guest_image->load_paddr = load_address; 371 guest_image->size = module_size; 372 373 return 0; 374} 375