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