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