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