1 /*- 2 * Copyright (c) 2021 Roger Pau Monné <royger@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 /* 28 * This multiboot2 implementation only implements a subset of the full 29 * multiboot2 specification in order to be able to boot Xen and a 30 * FreeBSD Dom0. Trying to use it to boot other multiboot2 compliant 31 * kernels will most surely fail. 32 * 33 * The full multiboot specification can be found here: 34 * https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html 35 */ 36 37 #include <sys/param.h> 38 #include <sys/exec.h> 39 #include <sys/linker.h> 40 #include <sys/module.h> 41 #include <sys/stdint.h> 42 #define _MACHINE_ELF_WANT_32BIT 43 #include <machine/elf.h> 44 #include <machine/metadata.h> 45 #include <string.h> 46 #include <stand.h> 47 48 #include <efi.h> 49 #include <efilib.h> 50 51 #include "bootstrap.h" 52 #include "multiboot2.h" 53 #include "loader_efi.h" 54 55 extern int elf32_loadfile_raw(char *filename, uint64_t dest, 56 struct preloaded_file **result, int multiboot); 57 extern int elf64_load_modmetadata(struct preloaded_file *fp, uint64_t dest); 58 extern int elf64_obj_loadfile(char *filename, uint64_t dest, 59 struct preloaded_file **result); 60 extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp, 61 bool exit_bs); 62 63 extern void multiboot2_exec(void *entry, uint64_t multiboot_info, 64 uint64_t stack); 65 66 /* 67 * Multiboot2 header information to pass between the loading and the exec 68 * functions. 69 */ 70 struct mb2hdr { 71 uint32_t efi64_entry; 72 }; 73 74 static int 75 loadfile(char *filename, uint64_t dest, struct preloaded_file **result) 76 { 77 unsigned int i; 78 int error, fd; 79 void *header_search = NULL; 80 void *multiboot = NULL; 81 ssize_t search_size; 82 struct multiboot_header *header; 83 char *cmdline; 84 struct mb2hdr hdr; 85 bool keep_bs = false; 86 87 /* 88 * Read MULTIBOOT_SEARCH size in order to search for the 89 * multiboot magic header. 90 */ 91 if (filename == NULL) 92 return (EFTYPE); 93 if ((fd = open(filename, O_RDONLY)) == -1) 94 return (errno); 95 header_search = malloc(MULTIBOOT_SEARCH); 96 if (header_search == NULL) { 97 error = ENOMEM; 98 goto out; 99 } 100 search_size = read(fd, header_search, MULTIBOOT_SEARCH); 101 102 for (i = 0; i < search_size; i += MULTIBOOT_HEADER_ALIGN) { 103 header = header_search + i; 104 if (header->magic == MULTIBOOT2_HEADER_MAGIC) 105 break; 106 } 107 108 if (i >= search_size) { 109 error = EFTYPE; 110 goto out; 111 } 112 113 /* Valid multiboot header has been found, validate checksum */ 114 if (header->magic + header->architecture + header->header_length + 115 header->checksum != 0) { 116 printf("Multiboot checksum failed, magic: %#x " 117 "architecture: %#x header_length %#x checksum: %#x\n", 118 header->magic, header->architecture, header->header_length, 119 header->checksum); 120 error = EFTYPE; 121 goto out; 122 } 123 124 if (header->architecture != MULTIBOOT2_ARCHITECTURE_I386) { 125 printf("Unsupported architecture: %#x\n", 126 header->architecture); 127 error = EFTYPE; 128 goto out; 129 } 130 131 multiboot = malloc(header->header_length - sizeof(*header)); 132 error = lseek(fd, i + sizeof(*header), SEEK_SET); 133 if (error != i + sizeof(*header)) { 134 printf("Unable to set file pointer to header location: %d\n", 135 error); 136 goto out; 137 } 138 search_size = read(fd, multiboot, 139 header->header_length - sizeof(*header)); 140 141 bzero(&hdr, sizeof(hdr)); 142 for (i = 0; i < search_size; ) { 143 struct multiboot_header_tag *tag; 144 struct multiboot_header_tag_entry_address *entry; 145 struct multiboot_header_tag_information_request *req; 146 unsigned int j; 147 148 tag = multiboot + i; 149 150 switch(tag->type) { 151 case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST: 152 req = (void *)tag; 153 for (j = 0; 154 j < (tag->size - sizeof(*tag)) / sizeof(uint32_t); 155 j++) { 156 switch (req->requests[j]) { 157 case MULTIBOOT_TAG_TYPE_MMAP: 158 case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: 159 /* Only applicable to BIOS. */ 160 break; 161 162 case MULTIBOOT_TAG_TYPE_EFI_BS: 163 case MULTIBOOT_TAG_TYPE_EFI64: 164 case MULTIBOOT_TAG_TYPE_EFI64_IH: 165 /* Tags unconditionally added. */ 166 break; 167 168 default: 169 if (req->flags & 170 MULTIBOOT_HEADER_TAG_OPTIONAL) 171 break; 172 173 printf( 174 "Unknown non-optional information request %u\n", 175 req->requests[j]); 176 error = EINVAL; 177 goto out; 178 } 179 } 180 break; 181 182 case MULTIBOOT_HEADER_TAG_EFI_BS: 183 /* Never shut down BS. */ 184 keep_bs = true; 185 break; 186 187 case MULTIBOOT_HEADER_TAG_MODULE_ALIGN: 188 /* We will align modules by default already. */ 189 case MULTIBOOT_HEADER_TAG_END: 190 break; 191 192 case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64: 193 entry = (void *)tag; 194 hdr.efi64_entry = entry->entry_addr; 195 break; 196 197 default: 198 if (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL) 199 break; 200 printf("Unknown header tag %#x not optional\n", 201 tag->type); 202 error = EINVAL; 203 goto out; 204 } 205 206 i += roundup2(tag->size, MULTIBOOT_TAG_ALIGN); 207 if (tag->type == MULTIBOOT_HEADER_TAG_END) 208 break; 209 } 210 211 if (hdr.efi64_entry == 0) { 212 printf("No EFI64 entry address provided\n"); 213 error = EINVAL; 214 goto out; 215 } 216 if (!keep_bs) { 217 printf("Unable to boot MB2 with BS exited\n"); 218 error = EINVAL; 219 goto out; 220 } 221 222 error = elf32_loadfile_raw(filename, dest, result, 1); 223 if (error != 0) { 224 printf( 225 "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", 226 error); 227 goto out; 228 } 229 230 file_addmetadata(*result, MODINFOMD_NOCOPY | MODINFOMD_MB2HDR, 231 sizeof(hdr), &hdr); 232 233 /* 234 * f_addr is already aligned to PAGE_SIZE, make sure 235 * f_size it's also aligned so when the modules are loaded 236 * they are aligned to PAGE_SIZE. 237 */ 238 (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); 239 240 out: 241 if (header_search != NULL) 242 free(header_search); 243 if (multiboot != NULL) 244 free(multiboot); 245 close(fd); 246 return (error); 247 } 248 249 static unsigned int add_string(void *buf, unsigned int type, const char *str) 250 { 251 struct multiboot_tag *tag; 252 253 tag = buf; 254 tag->type = type; 255 tag->size = sizeof(*tag) + strlen(str) + 1; 256 strcpy(buf + sizeof(*tag), str); 257 return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN)); 258 } 259 260 static unsigned int add_efi(void *buf) 261 { 262 struct multiboot_tag *bs; 263 struct multiboot_tag_efi64 *efi64; 264 struct multiboot_tag_efi64_ih *ih; 265 unsigned int len; 266 267 len = 0; 268 bs = buf; 269 bs->type = MULTIBOOT_TAG_TYPE_EFI_BS; 270 bs->size = sizeof(*bs); 271 len += roundup2(bs->size, MULTIBOOT_TAG_ALIGN); 272 273 efi64 = buf + len; 274 efi64->type = MULTIBOOT_TAG_TYPE_EFI64; 275 efi64->size = sizeof(*efi64); 276 efi64->pointer = (uintptr_t)ST; 277 len += roundup2(efi64->size, MULTIBOOT_TAG_ALIGN); 278 279 ih = buf + len; 280 ih->type = MULTIBOOT_TAG_TYPE_EFI64_IH; 281 ih->size = sizeof(*ih); 282 ih->pointer = (uintptr_t)IH; 283 284 return (len + roundup2(ih->size, MULTIBOOT_TAG_ALIGN)); 285 } 286 287 static unsigned int add_module(void *buf, vm_offset_t start, vm_offset_t end, 288 const char *cmdline) 289 { 290 struct multiboot_tag_module *mod; 291 292 mod = buf; 293 mod->type = MULTIBOOT_TAG_TYPE_MODULE; 294 mod->size = sizeof(*mod); 295 mod->mod_start = start; 296 mod->mod_end = end; 297 if (cmdline != NULL) 298 { 299 strcpy(buf + sizeof(*mod), cmdline); 300 mod->size += strlen(cmdline) + 1; 301 } 302 303 return (roundup2(mod->size, MULTIBOOT_TAG_ALIGN)); 304 } 305 306 static unsigned int add_end(void *buf) 307 { 308 struct multiboot_tag *tag; 309 310 tag = buf; 311 tag->type = MULTIBOOT_TAG_TYPE_END; 312 tag->size = sizeof(*tag); 313 314 return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN)); 315 } 316 317 static int 318 exec(struct preloaded_file *fp) 319 { 320 EFI_PHYSICAL_ADDRESS addr = 0; 321 EFI_PHYSICAL_ADDRESS stack = 0; 322 EFI_STATUS status; 323 void *multiboot_space; 324 vm_offset_t modulep, kernend, kern_base, 325 payload_base; 326 char *cmdline = NULL; 327 size_t len; 328 int error; 329 uint32_t *total_size; 330 struct file_metadata *md; 331 struct xen_header header; 332 struct mb2hdr *hdr; 333 334 335 _Static_assert(sizeof(header) <= PAGE_SIZE, "header too big"); 336 337 if ((md = file_findmetadata(fp, 338 MODINFOMD_NOCOPY | MODINFOMD_MB2HDR)) == NULL) { 339 printf("Missing Multiboot2 EFI64 entry point\n"); 340 return(EFTYPE); 341 } 342 hdr = (void *)&md->md_data; 343 344 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 345 EFI_SIZE_TO_PAGES(PAGE_SIZE), &addr); 346 if (EFI_ERROR(status)) { 347 printf("Failed to allocate pages for multiboot2 header: %lu\n", 348 EFI_ERROR_CODE(status)); 349 error = ENOMEM; 350 goto error; 351 } 352 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 353 EFI_SIZE_TO_PAGES(128 * 1024), &stack); 354 if (EFI_ERROR(status)) { 355 printf("Failed to allocate pages for Xen stack: %lu\n", 356 EFI_ERROR_CODE(status)); 357 error = ENOMEM; 358 goto error; 359 } 360 361 /* 362 * Scratch space to build the multiboot2 header. Reserve the start of 363 * the space to place the header with the size, which we don't know 364 * yet. 365 */ 366 multiboot_space = (void *)(uintptr_t)(addr + sizeof(uint32_t) * 2); 367 368 /* 369 * Don't pass the memory size found by the bootloader, the memory 370 * available to Dom0 will be lower than that. 371 */ 372 unsetenv("smbios.memory.enabled"); 373 374 /* Set the Xen command line. */ 375 if (fp->f_args == NULL) { 376 /* Add the Xen command line if it is set. */ 377 cmdline = getenv("xen_cmdline"); 378 if (cmdline != NULL) { 379 fp->f_args = strdup(cmdline); 380 if (fp->f_args == NULL) { 381 error = ENOMEM; 382 goto error; 383 } 384 } 385 } 386 if (fp->f_args != NULL) { 387 len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; 388 cmdline = malloc(len); 389 if (cmdline == NULL) { 390 error = ENOMEM; 391 goto error; 392 } 393 snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); 394 multiboot_space += add_string(multiboot_space, 395 MULTIBOOT_TAG_TYPE_CMDLINE, cmdline); 396 free(cmdline); 397 } 398 399 multiboot_space += add_string(multiboot_space, 400 MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME, "FreeBSD Loader"); 401 multiboot_space += add_efi(multiboot_space); 402 403 /* 404 * Prepare the multiboot module list, Xen assumes the first 405 * module is the Dom0 kernel, and the second one is the initramfs. 406 * This is not optimal for FreeBSD, that doesn't have a initramfs 407 * but instead loads modules dynamically and creates the metadata 408 * info on-the-fly. 409 * 410 * As expected, the first multiboot module is going to be the 411 * FreeBSD kernel loaded as a raw file. The second module is going 412 * to contain the metadata info and the loaded modules. 413 * 414 * There's a small header prefixed in the second module that contains 415 * some information required to calculate the relocated address of 416 * modulep based on the original offset of modulep from the start of 417 * the module address. Note other fields might be added to this header 418 * if required. 419 * 420 * Native layout: 421 * fp->f_addr + fp->f_size 422 * +---------+----------------+------------+ 423 * | | | | 424 * | Kernel | Modules | Metadata | 425 * | | | | 426 * +---------+----------------+------------+ 427 * fp->f_addr modulep kernend 428 * 429 * Xen dom0 layout: 430 * fp->f_addr fp->f_addr + fp->f_size 431 * +---------+------------+----------------+------------+ 432 * | | | | | 433 * | Kernel | xen_header | Modules | Metadata | 434 * | | | | | 435 * +---------+------------+----------------+------------+ 436 * modulep kernend 437 * \________/\__________________________________________/ 438 * module 0 module 1 439 */ 440 441 fp = file_findfile(NULL, "elf kernel"); 442 if (fp == NULL) { 443 printf("No FreeBSD kernel provided, aborting\n"); 444 error = EINVAL; 445 goto error; 446 } 447 448 error = bi_load(fp->f_args, &modulep, &kernend, false); 449 if (error != 0) 450 goto error; 451 452 /* 453 * Note that the Xen kernel requires to be started with BootServices 454 * enabled, and hence we cannot use efi_copy_finish to relocate the 455 * loaded data from the staging area to the expected loaded addresses. 456 * This is fine because the Xen kernel is relocatable, so it can boot 457 * fine straight from the staging area. We use efi_translate to get the 458 * staging addresses where the kernels and metadata are currently 459 * loaded. 460 */ 461 kern_base = (uintptr_t)efi_translate(fp->f_addr); 462 payload_base = kern_base + fp->f_size - PAGE_SIZE; 463 multiboot_space += add_module(multiboot_space, kern_base, payload_base, 464 NULL); 465 multiboot_space += add_module(multiboot_space, payload_base, 466 (uintptr_t)efi_translate(kernend), "header"); 467 468 header.flags = XENHEADER_HAS_MODULEP_OFFSET; 469 header.modulep_offset = modulep - (fp->f_addr + fp->f_size - PAGE_SIZE); 470 archsw.arch_copyin(&header, fp->f_addr + fp->f_size - PAGE_SIZE, 471 sizeof(header)); 472 473 multiboot_space += add_end(multiboot_space); 474 total_size = (uint32_t *)(uintptr_t)(addr); 475 *total_size = (uintptr_t)multiboot_space - addr; 476 477 if (*total_size > PAGE_SIZE) 478 panic("Multiboot header exceeds fixed size"); 479 480 efi_time_fini(); 481 dev_cleanup(); 482 multiboot2_exec(efi_translate(hdr->efi64_entry), addr, 483 stack + 128 * 1024); 484 485 panic("exec returned"); 486 487 error: 488 if (addr) 489 BS->FreePages(addr, EFI_SIZE_TO_PAGES(PAGE_SIZE)); 490 if (stack) 491 BS->FreePages(stack, EFI_SIZE_TO_PAGES(128 * 1024)); 492 return (error); 493 } 494 495 static int 496 obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result) 497 { 498 struct preloaded_file *mfp, *kfp, *rfp; 499 struct kernel_module *kmp; 500 int error; 501 502 /* See if there's a multiboot kernel loaded */ 503 mfp = file_findfile(NULL, "elf multiboot kernel"); 504 if (mfp == NULL) 505 return (EFTYPE); 506 507 /* 508 * We have a multiboot kernel loaded, see if there's a FreeBSD 509 * kernel loaded also. 510 */ 511 kfp = file_findfile(NULL, "elf kernel"); 512 if (kfp == NULL) { 513 /* 514 * No kernel loaded, this must be it. The kernel has to 515 * be loaded as a raw file, it will be processed by 516 * Xen and correctly loaded as an ELF file. 517 */ 518 rfp = file_loadraw(filename, "elf kernel", 0); 519 if (rfp == NULL) { 520 printf( 521 "Unable to load %s as a multiboot payload kernel\n", 522 filename); 523 return (EINVAL); 524 } 525 526 /* Load kernel metadata... */ 527 setenv("kernelname", filename, 1); 528 error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size); 529 if (error) { 530 printf("Unable to load kernel %s metadata error: %d\n", 531 rfp->f_name, error); 532 return (EINVAL); 533 } 534 535 536 /* 537 * Reserve one page at the end of the kernel to place some 538 * metadata in order to cope for Xen relocating the modules and 539 * the metadata information. 540 */ 541 rfp->f_size = roundup(rfp->f_size, PAGE_SIZE); 542 rfp->f_size += PAGE_SIZE; 543 *result = rfp; 544 } else { 545 /* The rest should be loaded as regular modules */ 546 error = elf64_obj_loadfile(filename, dest, result); 547 if (error != 0) { 548 printf("Unable to load %s as an object file, error: %d", 549 filename, error); 550 return (error); 551 } 552 } 553 554 return (0); 555 } 556 557 static int 558 obj_exec(struct preloaded_file *fp) 559 { 560 561 return (EFTYPE); 562 } 563 564 struct file_format multiboot2 = { loadfile, exec }; 565 struct file_format multiboot2_obj = { obj_loadfile, obj_exec }; 566