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