1/* 2 * Create a blob with a Multiboot2 image for the ARMv8 platform 3 * 4 * This tool reads menu.lst, loads a boot driver, a CPU kernel and modules, 5 * and assemble them into a Multiboot2 image. Adds also relocation info for 6 * the driver and the kernel. 7 * 8 * Copyright (c) 2016, ETH Zurich. 9 * All rights reserved. 10 * 11 * This file is distributed under the terms in the attached LICENSE file. 12 * If you do not find this file, copies can be found by writing to: 13 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 14 */ 15 16#include <stdio.h> 17#include <unistd.h> 18#include <fcntl.h> 19 20#include <sys/stat.h> 21#include <sys/types.h> 22 23#include <assert.h> 24#include <errno.h> 25#include <fcntl.h> 26#include <libelf.h> 27#include <limits.h> 28#include <stdarg.h> 29#include <stdint.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <unistd.h> 34 35/* We need to be able to parse menu.lst files, create multiboot images. */ 36#include "../../include/grubmenu.h" 37#include "../../include/multiboot2.h" 38#include "blob.h" 39 40#define DBG(format, ...) printf(format, ## __VA_ARGS__) 41 42/* Keep physical addresses and kernel virtual addresses separated, as far as 43 * possible. */ 44typedef uint64_t kvaddr_t; 45typedef uint64_t paddr_t; 46 47/*** A Linear Memory Allocator ***/ 48static paddr_t phys_alloc_start = 0; 49 50static size_t round_up(size_t x, size_t y) 51{ 52 size_t z = x + (y - 1); 53 return z - (z % y); 54} 55 56/* Advance the allocator to an address with the given alignment. */ 57static paddr_t align_alloc(paddr_t align) 58{ 59 phys_alloc_start = round_up(phys_alloc_start, align); 60 return phys_alloc_start; 61} 62 63/* Allocate an aligned block. */ 64static paddr_t phys_alloc(size_t size, size_t align) 65{ 66 align_alloc(align); 67 paddr_t addr = phys_alloc_start; 68 phys_alloc_start += size; 69 return addr; 70} 71 72/*** Failure Handling ***/ 73 74static void fail(const char *fmt, ...) 75{ 76 va_list ap; 77 va_start(ap, fmt); 78 vfprintf(stderr, fmt, ap); 79 va_end(ap); 80 exit(EXIT_FAILURE); 81} 82 83static void fail_errno(const char *fmt, ...) 84{ 85 char s[1024]; 86 87 va_list ap; 88 va_start(ap, fmt); 89 vsnprintf(s, 1024, fmt, ap); 90 va_end(ap); 91 92 perror(s); 93 exit(EXIT_FAILURE); 94} 95 96static void fail_elf(const char *s) 97{ 98 fprintf(stderr, "%s: %s\n", s, elf_errmsg(elf_errno())); 99 exit(EXIT_FAILURE); 100} 101 102static void join_paths(char *dst, const char *src1, const char *src2) 103{ 104 strcpy(dst, src1); 105 dst[strlen(src1)] = '/'; 106 strcpy(dst + strlen(src1) + 1, src2); 107} 108 109struct ram_region { 110 uint64_t base; 111 uint64_t npages; 112 void *buffer; 113}; 114 115struct loaded_module { 116 void *data; 117 paddr_t paddr; 118 size_t len, size; 119 const char *shortname; 120}; 121 122struct loaded_image { 123 struct ram_region segment; 124 125 size_t loaded_size; 126 paddr_t loaded_paddr; 127 kvaddr_t loaded_vaddr; 128 129 paddr_t entry; 130 const char *extrasym_name; 131 void *extrasym_ptr; 132 133 void *shdrs, *symtab, *strtab, *shstrtab; 134 size_t shdrs_size, symtab_size, strtab_size, shstrtab_size; 135 size_t shdrs_entsize, symtab_entsize; 136 137 unsigned no_relocations; 138 struct Blob_relocation *relocations; 139}; 140 141 142/* Load an ELF file as a raw data blob. */ 143void raw_load(const char *path, struct loaded_module *m) 144{ 145 struct stat mstat; 146 147 if (stat(path, &mstat)) 148 fail_errno("stat: %s", path); 149 150 size_t data_len = mstat.st_size; 151 m->len = round_up(data_len, BASE_PAGE_SIZE); 152 m->size = data_len; 153 m->data = calloc(m->len, 1); 154 if (!m->data) 155 fail_errno("calloc"); 156 m->paddr = phys_alloc(m->len, BASE_PAGE_SIZE); 157 158 printf("Allocated 0x%zx at PA %016zx for %s (%zd)\n", m->len, m->paddr, 159 path, data_len); 160 161 int fd = open(path, O_RDONLY); 162 if (fd < 0) 163 fail_errno("open: %s", path); 164 size_t read_len = read(fd, m->data, data_len); 165 if (read_len != data_len) 166 fail_errno("fread"); 167 close(fd); 168} 169 170/*** Multiboot ***/ 171 172#define ROUND_UP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) 173#define ALIGN(x) ROUND_UP((x), sizeof(uintptr_t)) 174 175/* Create the multiboot header, using only *physical* addresses. */ 176void *create_multiboot_info(struct menu_lst *menu, 177 struct loaded_module *modules, 178 size_t * mb_size, paddr_t * mb_base, 179 paddr_t entry) 180{ 181 size_t size; 182 unsigned i; 183 void *cursor; 184 185 /* Calculate the boot information size. */ 186 /* Multiboot2 information data structure */ 187 size = 8; 188 /* cpu driver command line */ 189 size += ALIGN(sizeof(struct multiboot_tag_string) 190 + strlen(menu->kernel.args) + 1); 191 // /* Boot driver module tag, including command line and ELF image */ 192 size += ALIGN(sizeof(struct multiboot_tag_module_64) 193 + strlen(menu->boot_driver.path) + 2); 194 // /* CPU driver module tag, including command line and ELF image */ 195 size += ALIGN(sizeof(struct multiboot_tag_module_64) 196 + strlen(menu->kernel.path) + strlen(menu->kernel.args) + 197 2); 198 /* All other modules */ 199 for (i = 0; i < menu->nmodules; i++) { 200 size += ALIGN(sizeof(struct multiboot_tag_module_64) 201 + strlen(menu->modules[i].path) + 202 strlen(menu->modules[i].args) + 2); 203 } 204#define MEM_MAP_SIZE (1<<13) 205 /* EFI memory map */ 206 size += ALIGN(sizeof(struct multiboot_tag_efi_mmap) + MEM_MAP_SIZE); 207 // END tag 208 size += ALIGN(sizeof(struct multiboot_tag)); 209 210 size_t allocated_size = round_up(size, BASE_PAGE_SIZE); 211 /* Allocate target addresses. */ 212 paddr_t base = phys_alloc(size, BASE_PAGE_SIZE); 213 *mb_size = allocated_size; 214 *mb_base = base; 215 216 /* Allocate our host buffer. */ 217 void *mb = calloc(allocated_size, 1); 218 if (!mb) 219 fail_errno("calloc"); 220 221 cursor = mb; 222 /* Skip the information structure for now */ 223 cursor += 8; 224 225 /* Add the boot command line */ 226 { 227 struct multiboot_tag_string *bootcmd = 228 (struct multiboot_tag_string *) cursor; 229 bootcmd->type = MULTIBOOT_TAG_TYPE_CMDLINE; 230 bootcmd->size = ALIGN(sizeof(struct multiboot_tag_string) 231 + strlen(menu->kernel.path) + 232 strlen(menu->kernel.args) + 2); 233 sprintf(bootcmd->string, "%s %s", menu->kernel.path, 234 menu->kernel.args); 235 cursor += bootcmd->size; 236 } 237 238 /* Add the boot driver module. */ 239 { 240 struct multiboot_tag_module_64 *boot_driver = 241 (struct multiboot_tag_module_64 *) cursor; 242 243 boot_driver->type = MULTIBOOT_TAG_TYPE_MODULE_64; 244 boot_driver->size = ALIGN(sizeof(struct multiboot_tag_module_64) 245 + strlen(menu->boot_driver.path) + 2); 246 boot_driver->mod_start = (multiboot_uint64_t) modules[0].paddr; 247 boot_driver->mod_end = 248 (multiboot_uint64_t) (modules[0].paddr + modules[0].size - 1); 249 sprintf(boot_driver->cmdline, "%s", menu->boot_driver.path); 250 cursor += boot_driver->size; 251 } 252 /* Add the kernel module. */ 253 { 254 struct multiboot_tag_module_64 *kernel = 255 (struct multiboot_tag_module_64 *) cursor; 256 257 kernel->type = MULTIBOOT_TAG_TYPE_MODULE_64; 258 kernel->size = ALIGN(sizeof(struct multiboot_tag_module_64) 259 + strlen(menu->kernel.path) + 260 strlen(menu->kernel.args) + 2); 261 kernel->mod_start = (multiboot_uint64_t) modules[1].paddr; 262 kernel->mod_end = 263 (multiboot_uint64_t) (modules[1].paddr + modules[1].size - 1); 264 sprintf(kernel->cmdline, "%s %s", menu->kernel.path, 265 menu->kernel.args); 266 cursor += kernel->size; 267 } 268 /* Add the remaining modules */ 269 for (i = 0; i < menu->nmodules; i++) { 270 struct multiboot_tag_module_64 *module = 271 (struct multiboot_tag_module_64 *) cursor; 272 273 module->type = MULTIBOOT_TAG_TYPE_MODULE_64; 274 module->size = ALIGN(sizeof(struct multiboot_tag_module_64) 275 + strlen(menu->modules[i].path) + 276 strlen(menu->modules[i].args) + 2); 277 module->mod_start = (multiboot_uint64_t) modules[i + 2].paddr; 278 module->mod_end = 279 (multiboot_uint64_t) (modules[i + 2].paddr + 280 modules[i + 2].size - 1); 281 sprintf(module->cmdline, "%s %s", menu->modules[i].path, 282 menu->modules[i].args); 283 cursor += module->size; 284 } 285 /* Add the EFI MMAP tag */ 286 { 287 struct multiboot_tag_efi_mmap *mmap_tag = 288 (struct multiboot_tag_efi_mmap *) cursor; 289 mmap_tag->type = MULTIBOOT_TAG_TYPE_EFI_MMAP; 290 cursor += sizeof(struct multiboot_tag_efi_mmap); 291 } 292 return mb; 293} 294 295int relocate_elf(struct ram_region *segment, Elf * elf, 296 Elf64_Phdr * phdr, size_t phnum, size_t shnum, 297 unsigned *no_relocations, 298 struct Blob_relocation **relocations) 299{ 300 size_t i; 301 302 *no_relocations = 0; 303 304 /* Search for relocaton sections. */ 305 for (i = 0; i < shnum; i++) { 306 Elf_Scn *scn = elf_getscn(elf, i); 307 if (!scn) { 308 printf("elf_getscn: %s\n", elf_errmsg(elf_errno())); 309 return -1; 310 } 311 312 Elf64_Shdr *shdr = elf64_getshdr(scn); 313 if (!shdr) { 314 printf("elf64_getshdr: %s\n", elf_errmsg(elf_errno())); 315 return -1; 316 } 317 if (shdr->sh_type == SHT_DYNAMIC) { 318 int relocations_size; 319 Elf_Data *data = elf_getdata(scn, NULL); 320 Elf64_Dyn *dt = (Elf64_Dyn *) data->d_buf; 321 for (; dt->d_tag && dt->d_tag != DT_RELACOUNT; dt++) { 322 } 323 assert(dt->d_tag == DT_RELACOUNT); 324 *no_relocations = dt->d_un.d_val; 325 relocations_size = 326 round_up(*no_relocations * sizeof(struct Blob_relocation), 327 BASE_PAGE_SIZE); 328 *relocations = malloc(relocations_size); 329 } else if (shdr->sh_type == SHT_RELA) { 330 if (shdr->sh_info != 0) { 331 printf("I expected global relocations, but got" 332 " section-specific ones.\n"); 333 return -1; 334 } 335 336 /* Hardcoded for one loadable segment. 337 XXX: seems to be not always the case for some ARMv8 builids. 338 */ 339 //ASSERT(phnum == 1); 340 341 Elf64_Addr segment_elf_base = phdr[0].p_vaddr; 342 Elf64_Addr segment_load_base = segment->base; 343 Elf64_Sxword segment_delta = 344 segment_load_base - segment_elf_base; 345 346 /* Walk the section data descriptors. */ 347 Elf_Data *reldata; 348 for (reldata = elf_getdata(scn, NULL); 349 reldata; reldata = elf_getdata(scn, reldata)) { 350 size_t rsize; 351 if (shdr->sh_type == SHT_REL) 352 rsize = sizeof(Elf64_Rel); 353 else 354 rsize = sizeof(Elf64_Rela); 355 356 size_t nrel = reldata->d_size / rsize; 357 358 /* Iterate through the relocations. */ 359 size_t i; 360 for (i = 0; i < nrel; i++) { 361 void *reladdr = reldata->d_buf + i * rsize; 362 Elf64_Addr offset; 363 Elf64_Xword sym, type; 364 Elf64_Sxword addend; 365 366 assert(shdr->sh_type == SHT_RELA); 367 Elf64_Rela *rel = reladdr; 368 369 offset = rel->r_offset; 370 sym = ELF64_R_SYM(rel->r_info); 371 type = ELF64_R_TYPE(rel->r_info); 372 addend = rel->r_addend; 373 374 assert(type == R_AARCH64_RELATIVE); 375 if (sym != 0) { 376 printf("Relocation references a" 377 " dynamic symbol, which is" 378 " unsupported.\n"); 379 return -1; 380 } 381 382 /* Delta(S) + A */ 383 (*relocations)[i].offset = offset; 384 (*relocations)[i].addend = addend; 385 } 386 } 387 } 388 } 389 390 return 0; 391} 392 393/* Load and relocate an ELF, with the given offset between the physical 394 * address at which it is loaded, and the virtual address at which it 395 * executes. For the boot driver, the offset is zero. Return a variety of 396 * information about the loaded image. */ 397static void load(struct loaded_module *module, uint32_t vp_offset, 398 struct loaded_image *image, int save_sections) 399{ 400 int i; 401 /* Open the ELF. */ 402 Elf *elf = elf_memory(module->data, module->size); 403 if (!elf) 404 fail_elf("elf_begin"); 405 406 /* Grab the unrelocated entry address from the header. */ 407 Elf64_Ehdr *ehdr = elf64_getehdr(elf); 408 if (!ehdr) 409 fail_elf("elf64_getehdr"); 410 image->entry = ehdr->e_entry; 411 412 /* Grab the program headers i.e. the list of loadable segments. */ 413 size_t phnum; 414 if (elf_getphdrnum(elf, &phnum)) 415 fail_elf("elf_getphnum"); 416 417 Elf64_Phdr *phdr = elf64_getphdr(elf); 418 if (!phdr) 419 fail_elf("elf_getphdr"); 420 421 DBG("%zd program segments.\n", phnum); 422 423 /* Grab the raw ELF data. */ 424 size_t elfsize; 425 void *elfdata = elf_rawfile(elf, &elfsize); 426 if (!elfdata) 427 fail_elf("elf_rawfile"); 428 429 /* Count the loadable segments, to allocate the region list. */ 430 size_t nloadsegs = 0; 431 for (i = 0; i < phnum; i++) { 432 if (phdr[i].p_type == PT_LOAD) 433 nloadsegs++; 434 } 435 436 for (i = 0; i < phnum; i++) { 437 printf 438 ("Segment %d load address %zx, offset %zx, file size %zx, memory size %zx\n", 439 i, phdr[i].p_vaddr, phdr[i].p_offset, phdr[i].p_filesz, 440 phdr[i].p_memsz); 441 if (phdr[i].p_type != PT_LOAD) 442 continue; 443 444 unsigned p_pages = 445 round_up(phdr[i].p_memsz, BASE_PAGE_SIZE) / BASE_PAGE_SIZE; 446 void *p_buf; 447 448 paddr_t pa = phys_alloc(phdr[i].p_memsz, BASE_PAGE_SIZE); 449 p_buf = calloc(p_pages * BASE_PAGE_SIZE, 1); 450 assert(p_buf); 451 452 image->segment.buffer = p_buf; 453 image->segment.base = pa; 454 image->segment.npages = p_pages; 455 456 memcpy(p_buf, module->data + phdr[i].p_offset, phdr[i].p_filesz); 457 } 458 459 size_t shnum; 460 int status; 461 status = elf_getshdrnum(elf, &shnum); 462 if (status) { 463 printf("elf_getshdrnum: %s\n", elf_errmsg(elf_errno())); 464 assert(0); 465 } 466 467 status = 468 relocate_elf(&image->segment, elf, phdr, phnum, shnum, 469 &image->no_relocations, &image->relocations); 470 if (status) { 471 printf("Relocation failed.\n"); 472 assert(0); 473 } 474 elf_end(elf); 475} 476 477 478int main(int argc, char *argv[]) 479{ 480 char pathbuf[PATH_MAX + 1]; 481 482 // if(argc != 6) usage(argv[0]); 483 484 const char *menu_lst = argv[1], 485 *outfile = argv[2], *buildroot = argv[3]; 486 487 errno = 0; 488 489 printf("ARMv8 Static Bootloader\n"); 490 491 /* Read the menu.lst file. */ 492 printf("Reading boot configuration from %s\n", menu_lst); 493 struct menu_lst *menu = read_menu_lst(menu_lst); 494 495 struct loaded_module *modules = 496 calloc(menu->nmodules + 2, sizeof(struct loaded_module)); 497 if (!modules) 498 fail_errno("calloc"); 499 500 // create the Blob 501 paddr_t base = phys_alloc(sizeof(struct Blob), BASE_PAGE_SIZE); 502 printf("Blob info struct at PA %016lx\n", base); 503 504 // Load the boot driver 505 join_paths(pathbuf, buildroot, menu->boot_driver.path); 506 raw_load(pathbuf, modules); 507 508 /* Use the filename as a short identifier. */ 509 const char *lastslash = strrchr(menu->boot_driver.path, '/'); 510 if (lastslash) { 511 modules[0].shortname = lastslash + 1; 512 } else { 513 modules[0].shortname = ""; 514 } 515 // Load the kernel 516 join_paths(pathbuf, buildroot, menu->kernel.path); 517 raw_load(pathbuf, modules + 1); 518 519 /* Use the filename as a short identifier. */ 520 lastslash = strrchr(menu->kernel.path, '/'); 521 if (lastslash) { 522 modules[1].shortname = lastslash + 1; 523 } else { 524 modules[1].shortname = ""; 525 } 526 527 /*** Load the modules. ***/ 528 529 for (size_t i = 0; i < menu->nmodules; i++) { 530 join_paths(pathbuf, buildroot, menu->modules[i].path); 531 raw_load(pathbuf, modules + i + 2); 532 533 /* Use the filename as a short identifier. */ 534 lastslash = strrchr(menu->modules[i].path, '/'); 535 if (lastslash) { 536 modules[i + 2].shortname = lastslash + 1; 537 } else { 538 modules[i + 2].shortname = ""; 539 } 540 } 541 542 if (elf_version(EV_CURRENT) == EV_NONE) 543 fail("ELF library version out of date.\n"); 544 /*** Load the boot driver. ***/ 545 546 /* Load and relocate it. */ 547 struct loaded_image bd_image[2]; 548 bd_image[0].extrasym_name = "boot_arguments"; 549 load(modules, 0, bd_image, 1); 550 load(modules + 1, 0, bd_image + 1, 1); 551 552 printf("Boot driver entry point: PA %08zx\n", bd_image[0].entry); 553 printf("CPU driver entry point: PA %08zx\n", bd_image[1].entry); 554 555 paddr_t pa, endpa; 556 struct Blob blob; 557 558 memset(blob.data, 0, sizeof(blob.data)); 559 blob.magic = 0x12345678fedcba90; 560 561 pa = phys_alloc(bd_image[0].no_relocations * 562 sizeof(struct Blob_relocation), BASE_PAGE_SIZE); 563 printf("Boot relocations PA %016zx,%d\n", pa, 564 bd_image[0].no_relocations); 565 blob.boot_driver_relocations = pa; 566 blob.boot_driver_relocations_count = bd_image[0].no_relocations; 567 blob.boot_driver_segment = bd_image[0].segment.base; 568 blob.boot_driver_segment_size = bd_image[0].segment.npages * BASE_PAGE_SIZE; 569 blob.boot_driver_entry = (uint64_t)bd_image[0].entry; 570 571 pa = phys_alloc(bd_image[1].no_relocations * 572 sizeof(struct Blob_relocation), BASE_PAGE_SIZE); 573 printf("Kernel relocations PA %016zx,%d\n", pa, 574 bd_image[1].no_relocations); 575 blob.cpu_driver_relocations = pa; 576 blob.cpu_driver_relocations_count = bd_image[1].no_relocations; 577 blob.cpu_driver_segment = bd_image[1].segment.base; 578 blob.cpu_driver_segment_size = bd_image[1].segment.npages * BASE_PAGE_SIZE; 579 blob.cpu_driver_entry = (uint64_t)bd_image[1].entry; 580 581 /*** Create the multiboot info header. ***/ 582 size_t mb_size, size; 583 paddr_t mb_base; 584 void *mb_image = 585 create_multiboot_info(menu, modules, &mb_size, &mb_base, 586 bd_image[1].entry); 587 588 endpa = phys_alloc(BASE_PAGE_SIZE, BASE_PAGE_SIZE); 589 printf("Final PA %016zx\n", endpa); 590 591 blob.multiboot = mb_base; 592 blob.multiboot_size = mb_size; 593 594 blob.modules = modules[0].paddr; 595 for (size_t i = 0; i < menu->nmodules + 2; i++) { 596 blob.modules_size += modules[i].len; 597 } 598 599 size_t r; 600 FILE *fp = fopen(outfile, "wb"); 601 assert(fp >= 0); 602 // write the blob info 603 r = fwrite(&blob, 1, BASE_PAGE_SIZE, fp); 604 assert(r == BASE_PAGE_SIZE); 605 // write the modules 606 for (size_t i = 0; i < menu->nmodules + 2; i++) { 607 r = fwrite(modules[i].data, 1, modules[i].len, fp); 608 assert(r == modules[i].len); 609 } 610 // write the boot driver's ELF section 611 r = fwrite(bd_image[0].segment.buffer, 1, 612 bd_image[0].segment.npages * BASE_PAGE_SIZE, fp); 613 assert(r == bd_image[0].segment.npages * BASE_PAGE_SIZE); 614 // write the kernel's ELF section 615 r = fwrite(bd_image[1].segment.buffer, 1, 616 bd_image[1].segment.npages * BASE_PAGE_SIZE, fp); 617 assert(r == bd_image[1].segment.npages * BASE_PAGE_SIZE); 618 // write the boot driver's relocations 619 size = 620 round_up(bd_image[0].no_relocations * 621 sizeof(struct Blob_relocation), BASE_PAGE_SIZE); 622 r = fwrite(bd_image[0].relocations, 1, size, fp); 623 assert(r == size); 624 // write the kernel's relocations 625 size = 626 round_up(bd_image[1].no_relocations * 627 sizeof(struct Blob_relocation), BASE_PAGE_SIZE); 628 r = fwrite(bd_image[1].relocations, 1, size, fp); 629 assert(r == size); 630 // write the multiboot info 631 r = fwrite(mb_image, 1, mb_size, fp); 632 assert(r == mb_size); 633 fclose(fp); 634 635 return 0; 636} 637