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