1277215Sroyger/*- 2277215Sroyger * Copyright (c) 2014 Roger Pau Monn�� <royger@FreeBSD.org> 3277215Sroyger * All rights reserved. 4277215Sroyger * 5277215Sroyger * Redistribution and use in source and binary forms, with or without 6277215Sroyger * modification, are permitted provided that the following conditions 7277215Sroyger * are met: 8277215Sroyger * 1. Redistributions of source code must retain the above copyright 9277215Sroyger * notice, this list of conditions and the following disclaimer. 10277215Sroyger * 2. Redistributions in binary form must reproduce the above copyright 11277215Sroyger * notice, this list of conditions and the following disclaimer in the 12277215Sroyger * documentation and/or other materials provided with the distribution. 13277215Sroyger * 14277215Sroyger * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15277215Sroyger * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16277215Sroyger * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17277215Sroyger * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18277215Sroyger * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19277215Sroyger * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20277215Sroyger * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21277215Sroyger * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22277215Sroyger * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23277215Sroyger * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24277215Sroyger * SUCH DAMAGE. 25277215Sroyger */ 26277215Sroyger 27277215Sroyger/* 28277215Sroyger * This multiboot implementation only implements a subset of the full 29277215Sroyger * multiboot specification in order to be able to boot Xen and a 30277215Sroyger * FreeBSD Dom0. Trying to use it to boot other multiboot compliant 31277215Sroyger * kernels will most surely fail. 32277215Sroyger * 33277215Sroyger * The full multiboot specification can be found here: 34277215Sroyger * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html 35277215Sroyger */ 36277215Sroyger 37277215Sroyger#include <sys/cdefs.h> 38277215Sroyger__FBSDID("$FreeBSD$"); 39277215Sroyger 40277215Sroyger#include <sys/param.h> 41277215Sroyger#include <sys/exec.h> 42277215Sroyger#include <sys/linker.h> 43277215Sroyger#include <sys/module.h> 44277215Sroyger#include <sys/stdint.h> 45277215Sroyger#define _MACHINE_ELF_WANT_32BIT 46277215Sroyger#include <machine/elf.h> 47277215Sroyger#include <string.h> 48277215Sroyger#include <stand.h> 49277215Sroyger 50277215Sroyger#include "bootstrap.h" 51277215Sroyger#include "multiboot.h" 52277215Sroyger#include "../i386/libi386/libi386.h" 53277215Sroyger#include "../i386/btx/lib/btxv86.h" 54277215Sroyger 55277215Sroyger#define MULTIBOOT_SUPPORTED_FLAGS \ 56277215Sroyger (MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO) 57277215Sroyger#define NUM_MODULES 2 58277215Sroyger#define METADATA_FIXED_SIZE (PAGE_SIZE*4) 59277215Sroyger#define METADATA_MODULE_SIZE PAGE_SIZE 60277215Sroyger 61277215Sroyger#define METADATA_RESV_SIZE(mod_num) \ 62277215Sroyger roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE) 63277215Sroyger 64277215Sroygerextern int elf32_loadfile_raw(char *filename, u_int64_t dest, 65277215Sroyger struct preloaded_file **result, int multiboot); 66277215Sroygerextern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest); 67277215Sroygerextern int elf64_obj_loadfile(char *filename, u_int64_t dest, 68277215Sroyger struct preloaded_file **result); 69277215Sroyger 70277215Sroygerstatic int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **); 71277215Sroygerstatic int multiboot_exec(struct preloaded_file *); 72277215Sroyger 73277215Sroygerstatic int multiboot_obj_loadfile(char *, u_int64_t, struct preloaded_file **); 74277215Sroygerstatic int multiboot_obj_exec(struct preloaded_file *fp); 75277215Sroyger 76277215Sroygerstruct file_format multiboot = { multiboot_loadfile, multiboot_exec }; 77277215Sroygerstruct file_format multiboot_obj = 78277215Sroyger { multiboot_obj_loadfile, multiboot_obj_exec }; 79277215Sroyger 80277215Sroygerextern void multiboot_tramp(); 81277215Sroyger 82277215Sroygerstatic const char mbl_name[] = "FreeBSD Loader"; 83277215Sroyger 84277215Sroygerstatic int 85277215Sroygernum_modules(struct preloaded_file *kfp) 86277215Sroyger{ 87277215Sroyger struct kernel_module *kmp; 88277215Sroyger int mod_num = 0; 89277215Sroyger 90277215Sroyger for (kmp = kfp->f_modules; kmp != NULL; kmp = kmp->m_next) 91277215Sroyger mod_num++; 92277215Sroyger 93277215Sroyger return (mod_num); 94277215Sroyger} 95277215Sroyger 96277215Sroygerstatic vm_offset_t 97277215Sroygermax_addr(void) 98277215Sroyger{ 99277215Sroyger struct preloaded_file *fp; 100277215Sroyger vm_offset_t addr = 0; 101277215Sroyger 102277215Sroyger for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { 103277215Sroyger if (addr < (fp->f_addr + fp->f_size)) 104277215Sroyger addr = fp->f_addr + fp->f_size; 105277215Sroyger } 106277215Sroyger 107277215Sroyger return (addr); 108277215Sroyger} 109277215Sroyger 110277215Sroygerstatic int 111277215Sroygermultiboot_loadfile(char *filename, u_int64_t dest, 112277215Sroyger struct preloaded_file **result) 113277215Sroyger{ 114277215Sroyger uint32_t *magic; 115277215Sroyger int i, error; 116277215Sroyger caddr_t header_search; 117277215Sroyger ssize_t search_size; 118277215Sroyger int fd; 119277215Sroyger struct multiboot_header *header; 120277215Sroyger char *cmdline; 121277215Sroyger 122277215Sroyger /* 123277215Sroyger * Read MULTIBOOT_SEARCH size in order to search for the 124277215Sroyger * multiboot magic header. 125277215Sroyger */ 126277215Sroyger if (filename == NULL) 127277215Sroyger return (EFTYPE); 128277215Sroyger if ((fd = open(filename, O_RDONLY)) == -1) 129277215Sroyger return (errno); 130277215Sroyger header_search = malloc(MULTIBOOT_SEARCH); 131277215Sroyger if (header_search == NULL) { 132277215Sroyger close(fd); 133277215Sroyger return (ENOMEM); 134277215Sroyger } 135277215Sroyger search_size = read(fd, header_search, MULTIBOOT_SEARCH); 136277215Sroyger magic = (uint32_t *)header_search; 137277215Sroyger 138277215Sroyger header = NULL; 139277215Sroyger for (i = 0; i < (search_size / sizeof(uint32_t)); i++) { 140277215Sroyger if (magic[i] == MULTIBOOT_HEADER_MAGIC) { 141277215Sroyger header = (struct multiboot_header *)&magic[i]; 142277215Sroyger break; 143277215Sroyger } 144277215Sroyger } 145277215Sroyger 146277215Sroyger if (header == NULL) { 147277215Sroyger error = EFTYPE; 148277215Sroyger goto out; 149277215Sroyger } 150277215Sroyger 151277215Sroyger /* Valid multiboot header has been found, validate checksum */ 152277215Sroyger if (header->magic + header->flags + header->checksum != 0) { 153277215Sroyger printf( 154277215Sroyger "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n", 155277215Sroyger header->magic, header->flags, header->checksum); 156277215Sroyger error = EFTYPE; 157277215Sroyger goto out; 158277215Sroyger } 159277215Sroyger 160277215Sroyger if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) { 161277215Sroyger printf("Unsupported multiboot flags found: 0x%x\n", 162277215Sroyger header->flags); 163277215Sroyger error = EFTYPE; 164277215Sroyger goto out; 165277215Sroyger } 166277215Sroyger 167277215Sroyger error = elf32_loadfile_raw(filename, dest, result, 1); 168277215Sroyger if (error != 0) { 169277215Sroyger printf( 170277215Sroyger "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", 171277215Sroyger error); 172277215Sroyger goto out; 173277215Sroyger } 174277215Sroyger 175277215Sroyger /* 176277215Sroyger * f_addr is already aligned to PAGE_SIZE, make sure 177277215Sroyger * f_size it's also aligned so when the modules are loaded 178277215Sroyger * they are aligned to PAGE_SIZE. 179277215Sroyger */ 180277215Sroyger (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); 181277215Sroyger 182277215Sroygerout: 183277215Sroyger free(header_search); 184277215Sroyger close(fd); 185277215Sroyger return (error); 186277215Sroyger} 187277215Sroyger 188277215Sroygerstatic int 189277215Sroygermultiboot_exec(struct preloaded_file *fp) 190277215Sroyger{ 191277215Sroyger vm_offset_t module_start, last_addr, metadata_size; 192277215Sroyger vm_offset_t modulep, kernend, entry; 193277215Sroyger struct file_metadata *md; 194277215Sroyger Elf_Ehdr *ehdr; 195277215Sroyger struct multiboot_info *mb_info = NULL; 196277215Sroyger struct multiboot_mod_list *mb_mod = NULL; 197277215Sroyger char *cmdline = NULL; 198277215Sroyger size_t len; 199277215Sroyger int error, mod_num; 200277215Sroyger 201277215Sroyger /* 202277215Sroyger * Don't pass the memory size found by the bootloader, the memory 203277215Sroyger * available to Dom0 will be lower than that. 204277215Sroyger */ 205277215Sroyger unsetenv("smbios.memory.enabled"); 206277215Sroyger 207277215Sroyger /* Allocate the multiboot struct and fill the basic details. */ 208277215Sroyger mb_info = malloc(sizeof(struct multiboot_info)); 209277215Sroyger if (mb_info == NULL) { 210277215Sroyger error = ENOMEM; 211277215Sroyger goto error; 212277215Sroyger } 213277215Sroyger bzero(mb_info, sizeof(struct multiboot_info)); 214277215Sroyger mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME; 215277215Sroyger mb_info->mem_lower = bios_basemem / 1024; 216277215Sroyger mb_info->mem_upper = bios_extmem / 1024; 217277215Sroyger mb_info->boot_loader_name = VTOP(mbl_name); 218277215Sroyger 219277215Sroyger /* Set the Xen command line. */ 220277215Sroyger if (fp->f_args == NULL) { 221277215Sroyger /* Add the Xen command line if it is set. */ 222277215Sroyger cmdline = getenv("xen_cmdline"); 223277215Sroyger if (cmdline != NULL) { 224277215Sroyger fp->f_args = strdup(cmdline); 225277215Sroyger if (fp->f_args == NULL) { 226277215Sroyger error = ENOMEM; 227277215Sroyger goto error; 228277215Sroyger } 229277215Sroyger } 230277215Sroyger } 231277215Sroyger if (fp->f_args != NULL) { 232277215Sroyger len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; 233277215Sroyger cmdline = malloc(len); 234277215Sroyger if (cmdline == NULL) { 235277215Sroyger error = ENOMEM; 236277215Sroyger goto error; 237277215Sroyger } 238277215Sroyger snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); 239277215Sroyger mb_info->cmdline = VTOP(cmdline); 240277215Sroyger mb_info->flags |= MULTIBOOT_INFO_CMDLINE; 241277215Sroyger } 242277215Sroyger 243277215Sroyger /* Find the entry point of the Xen kernel and save it for later */ 244277215Sroyger if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { 245277215Sroyger printf("Unable to find %s entry point\n", fp->f_name); 246294417Sroyger error = EINVAL; 247277215Sroyger goto error; 248277215Sroyger } 249277215Sroyger ehdr = (Elf_Ehdr *)&(md->md_data); 250277215Sroyger entry = ehdr->e_entry & 0xffffff; 251277215Sroyger 252277215Sroyger /* 253277215Sroyger * Prepare the multiboot module list, Xen assumes the first 254277215Sroyger * module is the Dom0 kernel, and the second one is the initramfs. 255277215Sroyger * This is not optimal for FreeBSD, that doesn't have a initramfs 256277215Sroyger * but instead loads modules dynamically and creates the metadata 257277215Sroyger * info on-the-fly. 258277215Sroyger * 259277215Sroyger * As expected, the first multiboot module is going to be the 260277215Sroyger * FreeBSD kernel loaded as a raw file. The second module is going 261277215Sroyger * to contain the metadata info and the loaded modules. 262277215Sroyger * 263277215Sroyger * On native FreeBSD loads all the modules and then places the 264277215Sroyger * metadata info at the end, but this is painful when running on Xen, 265277215Sroyger * because it relocates the second multiboot module wherever it 266277215Sroyger * likes. In order to workaround this limitation the metadata 267277215Sroyger * information is placed at the start of the second module and 268277215Sroyger * the original modulep value is saved together with the other 269277215Sroyger * metadata, so we can relocate everything. 270277215Sroyger */ 271277215Sroyger fp = file_findfile(NULL, "elf kernel"); 272277215Sroyger if (fp == NULL) { 273277215Sroyger printf("No FreeBSD kernel provided, aborting\n"); 274294417Sroyger error = EINVAL; 275277215Sroyger goto error; 276277215Sroyger } 277294417Sroyger 278277215Sroyger mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES); 279294417Sroyger if (mb_mod == NULL) { 280294417Sroyger error = ENOMEM; 281294417Sroyger goto error; 282294417Sroyger } 283277215Sroyger 284294417Sroyger bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES); 285294417Sroyger 286277215Sroyger /* 287277215Sroyger * Calculate how much memory is needed for the metatdata. We did 288277215Sroyger * an approximation of the maximum size when loading the kernel, 289277215Sroyger * but now we know the exact size, so we can release some of this 290277215Sroyger * preallocated memory if not needed. 291277215Sroyger */ 292277215Sroyger last_addr = roundup(max_addr(), PAGE_SIZE); 293277215Sroyger mod_num = num_modules(fp); 294277215Sroyger 295277215Sroyger /* 296277215Sroyger * Place the metadata after the last used address in order to 297277215Sroyger * calculate it's size, this will not be used. 298277215Sroyger */ 299277215Sroyger error = bi_load64(fp->f_args, last_addr, &modulep, &kernend, 0); 300277215Sroyger if (error != 0) { 301277215Sroyger printf("bi_load64 failed: %d\n", error); 302277215Sroyger goto error; 303277215Sroyger } 304277215Sroyger metadata_size = roundup(kernend - last_addr, PAGE_SIZE); 305277215Sroyger 306277215Sroyger /* Check that the size is not greater than what we have reserved */ 307277215Sroyger if (metadata_size > METADATA_RESV_SIZE(mod_num)) { 308277215Sroyger printf("Required memory for metadata is greater than reserved " 309277215Sroyger "space, please increase METADATA_FIXED_SIZE and " 310277215Sroyger "METADATA_MODULE_SIZE and rebuild the loader\n"); 311277215Sroyger error = ENOMEM; 312277215Sroyger goto error; 313277215Sroyger } 314277215Sroyger 315277215Sroyger /* 316277215Sroyger * This is the position where the second multiboot module 317277215Sroyger * will be placed. 318277215Sroyger */ 319277215Sroyger module_start = fp->f_addr + fp->f_size - metadata_size; 320277215Sroyger 321277215Sroyger error = bi_load64(fp->f_args, module_start, &modulep, &kernend, 0); 322277215Sroyger if (error != 0) { 323277215Sroyger printf("bi_load64 failed: %d\n", error); 324277215Sroyger goto error; 325277215Sroyger } 326277215Sroyger 327277215Sroyger mb_mod[0].mod_start = fp->f_addr; 328277215Sroyger mb_mod[0].mod_end = fp->f_addr + fp->f_size; 329277215Sroyger mb_mod[0].mod_end -= METADATA_RESV_SIZE(mod_num); 330277215Sroyger 331277215Sroyger mb_mod[1].mod_start = module_start; 332277215Sroyger mb_mod[1].mod_end = last_addr; 333277215Sroyger 334277215Sroyger mb_info->mods_count = NUM_MODULES; 335277215Sroyger mb_info->mods_addr = VTOP(mb_mod); 336277215Sroyger mb_info->flags |= MULTIBOOT_INFO_MODS; 337277215Sroyger 338277215Sroyger dev_cleanup(); 339277215Sroyger __exec((void *)VTOP(multiboot_tramp), (void *)entry, 340277215Sroyger (void *)VTOP(mb_info)); 341277215Sroyger 342277215Sroyger panic("exec returned"); 343277215Sroyger 344277215Sroygererror: 345277215Sroyger if (mb_mod) 346277215Sroyger free(mb_mod); 347277215Sroyger if (mb_info) 348277215Sroyger free(mb_info); 349277215Sroyger if (cmdline) 350277215Sroyger free(cmdline); 351277215Sroyger return (error); 352277215Sroyger} 353277215Sroyger 354277215Sroygerstatic int 355277215Sroygermultiboot_obj_loadfile(char *filename, u_int64_t dest, 356277215Sroyger struct preloaded_file **result) 357277215Sroyger{ 358277215Sroyger struct preloaded_file *mfp, *kfp, *rfp; 359277215Sroyger struct kernel_module *kmp; 360277215Sroyger int error, mod_num; 361277215Sroyger 362277215Sroyger /* See if there's a multiboot kernel loaded */ 363277215Sroyger mfp = file_findfile(NULL, "elf multiboot kernel"); 364277215Sroyger if (mfp == NULL) 365277215Sroyger return (EFTYPE); 366277215Sroyger 367277215Sroyger /* 368277215Sroyger * We have a multiboot kernel loaded, see if there's a FreeBSD 369277215Sroyger * kernel loaded also. 370277215Sroyger */ 371277215Sroyger kfp = file_findfile(NULL, "elf kernel"); 372277215Sroyger if (kfp == NULL) { 373277215Sroyger /* 374277215Sroyger * No kernel loaded, this must be it. The kernel has to 375277215Sroyger * be loaded as a raw file, it will be processed by 376277215Sroyger * Xen and correctly loaded as an ELF file. 377277215Sroyger */ 378277215Sroyger rfp = file_loadraw(filename, "elf kernel", 0); 379277215Sroyger if (rfp == NULL) { 380277215Sroyger printf( 381277215Sroyger "Unable to load %s as a multiboot payload kernel\n", 382277215Sroyger filename); 383294417Sroyger return (EINVAL); 384277215Sroyger } 385277215Sroyger 386277215Sroyger /* Load kernel metadata... */ 387277215Sroyger setenv("kernelname", filename, 1); 388277215Sroyger error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size); 389277215Sroyger if (error) { 390277215Sroyger printf("Unable to load kernel %s metadata error: %d\n", 391277215Sroyger rfp->f_name, error); 392294417Sroyger return (EINVAL); 393277215Sroyger } 394277215Sroyger 395277215Sroyger /* 396277215Sroyger * Save space at the end of the kernel in order to place 397277215Sroyger * the metadata information. We do an approximation of the 398277215Sroyger * max metadata size, this is not optimal but it's probably 399277215Sroyger * the best we can do at this point. Once all modules are 400277215Sroyger * loaded and the size of the metadata is known this 401277215Sroyger * space will be recovered if not used. 402277215Sroyger */ 403277215Sroyger mod_num = num_modules(rfp); 404277215Sroyger rfp->f_size = roundup(rfp->f_size, PAGE_SIZE); 405277215Sroyger rfp->f_size += METADATA_RESV_SIZE(mod_num); 406277215Sroyger *result = rfp; 407277215Sroyger } else { 408277215Sroyger /* The rest should be loaded as regular modules */ 409277215Sroyger error = elf64_obj_loadfile(filename, dest, result); 410277215Sroyger if (error != 0) { 411277215Sroyger printf("Unable to load %s as an object file, error: %d", 412277215Sroyger filename, error); 413277215Sroyger return (error); 414277215Sroyger } 415277215Sroyger } 416277215Sroyger 417277215Sroyger return (0); 418277215Sroyger} 419277215Sroyger 420277215Sroygerstatic int 421277215Sroygermultiboot_obj_exec(struct preloaded_file *fp) 422277215Sroyger{ 423277215Sroyger 424277215Sroyger return (EFTYPE); 425277215Sroyger} 426