1 /*- 2 * Copyright (c) 2014 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 multiboot implementation only implements a subset of the full 29 * multiboot specification in order to be able to boot Xen and a 30 * FreeBSD Dom0. Trying to use it to boot other multiboot compliant 31 * kernels will most surely fail. 32 * 33 * The full multiboot specification can be found here: 34 * http://www.gnu.org/software/grub/manual/multiboot/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 "bootstrap.h" 49 #include "multiboot.h" 50 #include "libi386.h" 51 #include <btxv86.h> 52 53 #define MULTIBOOT_SUPPORTED_FLAGS \ 54 (MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO) 55 #define NUM_MODULES 2 56 57 extern int elf32_loadfile_raw(char *filename, uint64_t dest, 58 struct preloaded_file **result, int multiboot); 59 extern int elf64_load_modmetadata(struct preloaded_file *fp, uint64_t dest); 60 extern int elf64_obj_loadfile(char *filename, uint64_t dest, 61 struct preloaded_file **result); 62 63 static int multiboot_loadfile(char *, uint64_t, struct preloaded_file **); 64 static int multiboot_exec(struct preloaded_file *); 65 66 static int multiboot_obj_loadfile(char *, uint64_t, struct preloaded_file **); 67 static int multiboot_obj_exec(struct preloaded_file *fp); 68 69 struct file_format multiboot = { multiboot_loadfile, multiboot_exec }; 70 struct file_format multiboot_obj = 71 { multiboot_obj_loadfile, multiboot_obj_exec }; 72 73 extern void multiboot_tramp(); 74 75 static const char mbl_name[] = "FreeBSD Loader"; 76 77 static int 78 multiboot_loadfile(char *filename, uint64_t dest, 79 struct preloaded_file **result) 80 { 81 uint32_t *magic; 82 int i, error; 83 caddr_t header_search; 84 ssize_t search_size; 85 int fd; 86 struct multiboot_header *header; 87 char *cmdline; 88 89 /* 90 * Read MULTIBOOT_SEARCH size in order to search for the 91 * multiboot magic header. 92 */ 93 if (filename == NULL) 94 return (EFTYPE); 95 if ((fd = open(filename, O_RDONLY)) == -1) 96 return (errno); 97 header_search = malloc(MULTIBOOT_SEARCH); 98 if (header_search == NULL) { 99 close(fd); 100 return (ENOMEM); 101 } 102 search_size = read(fd, header_search, MULTIBOOT_SEARCH); 103 magic = (uint32_t *)header_search; 104 105 header = NULL; 106 for (i = 0; i < (search_size / sizeof(uint32_t)); i++) { 107 if (magic[i] == MULTIBOOT_HEADER_MAGIC) { 108 header = (struct multiboot_header *)&magic[i]; 109 break; 110 } 111 } 112 113 if (header == NULL) { 114 error = EFTYPE; 115 goto out; 116 } 117 118 /* Valid multiboot header has been found, validate checksum */ 119 if (header->magic + header->flags + header->checksum != 0) { 120 printf( 121 "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n", 122 header->magic, header->flags, header->checksum); 123 error = EFTYPE; 124 goto out; 125 } 126 127 if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) { 128 printf("Unsupported multiboot flags found: 0x%x\n", 129 header->flags); 130 error = EFTYPE; 131 goto out; 132 } 133 134 error = elf32_loadfile_raw(filename, dest, result, 1); 135 if (error != 0) { 136 printf( 137 "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", 138 error); 139 goto out; 140 } 141 142 /* 143 * f_addr is already aligned to PAGE_SIZE, make sure 144 * f_size it's also aligned so when the modules are loaded 145 * they are aligned to PAGE_SIZE. 146 */ 147 (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); 148 149 out: 150 free(header_search); 151 close(fd); 152 return (error); 153 } 154 155 static int 156 multiboot_exec(struct preloaded_file *fp) 157 { 158 vm_offset_t modulep, kernend, entry; 159 struct file_metadata *md; 160 Elf_Ehdr *ehdr; 161 struct multiboot_info *mb_info = NULL; 162 struct multiboot_mod_list *mb_mod = NULL; 163 char *cmdline = NULL; 164 size_t len; 165 int error, mod_num; 166 struct xen_header header; 167 168 _Static_assert(sizeof(header) <= PAGE_SIZE, "header too large for page"); 169 170 /* 171 * Don't pass the memory size found by the bootloader, the memory 172 * available to Dom0 will be lower than that. 173 */ 174 unsetenv("smbios.memory.enabled"); 175 176 /* Allocate the multiboot struct and fill the basic details. */ 177 mb_info = malloc(sizeof(struct multiboot_info)); 178 if (mb_info == NULL) { 179 error = ENOMEM; 180 goto error; 181 } 182 bzero(mb_info, sizeof(struct multiboot_info)); 183 mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME; 184 mb_info->mem_lower = bios_basemem / 1024; 185 mb_info->mem_upper = bios_extmem / 1024; 186 mb_info->boot_loader_name = VTOP(mbl_name); 187 188 /* Set the Xen command line. */ 189 if (fp->f_args == NULL) { 190 /* Add the Xen command line if it is set. */ 191 cmdline = getenv("xen_cmdline"); 192 if (cmdline != NULL) { 193 fp->f_args = strdup(cmdline); 194 if (fp->f_args == NULL) { 195 error = ENOMEM; 196 goto error; 197 } 198 } 199 } 200 if (fp->f_args != NULL) { 201 len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; 202 cmdline = malloc(len); 203 if (cmdline == NULL) { 204 error = ENOMEM; 205 goto error; 206 } 207 snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); 208 mb_info->cmdline = VTOP(cmdline); 209 mb_info->flags |= MULTIBOOT_INFO_CMDLINE; 210 } 211 212 /* Find the entry point of the Xen kernel and save it for later */ 213 if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { 214 printf("Unable to find %s entry point\n", fp->f_name); 215 error = EINVAL; 216 goto error; 217 } 218 ehdr = (Elf_Ehdr *)&(md->md_data); 219 entry = ehdr->e_entry & 0xffffff; 220 221 /* 222 * Prepare the multiboot module list, Xen assumes the first 223 * module is the Dom0 kernel, and the second one is the initramfs. 224 * This is not optimal for FreeBSD, that doesn't have a initramfs 225 * but instead loads modules dynamically and creates the metadata 226 * info on-the-fly. 227 * 228 * As expected, the first multiboot module is going to be the 229 * FreeBSD kernel loaded as a raw file. The second module is going 230 * to contain the metadata info and the loaded modules. 231 * 232 * There's a small header prefixed in the second module that contains 233 * some information required to calculate the relocated address of 234 * modulep based on the original offset of modulep from the start of 235 * the module address. Note other fields might be added to this header 236 * if required. 237 * 238 * Native layout: 239 * fp->f_addr + fp->f_size 240 * +---------+----------------+------------+ 241 * | | | | 242 * | Kernel | Modules | Metadata | 243 * | | | | 244 * +---------+----------------+------------+ 245 * fp->f_addr modulep kernend 246 * 247 * Xen dom0 layout: 248 * fp->f_addr fp->f_addr + fp->f_size 249 * +---------+------------+----------------+------------+ 250 * | | | | | 251 * | Kernel | xen_header | Modules | Metadata | 252 * | | | | | 253 * +---------+------------+----------------+------------+ 254 * modulep kernend 255 * \________/\__________________________________________/ 256 * module 0 module 1 257 */ 258 259 fp = file_findfile(NULL, "elf kernel"); 260 if (fp == NULL) { 261 printf("No FreeBSD kernel provided, aborting\n"); 262 error = EINVAL; 263 goto error; 264 } 265 266 mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES); 267 if (mb_mod == NULL) { 268 error = ENOMEM; 269 goto error; 270 } 271 272 bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES); 273 274 error = bi_load64(fp->f_args, &modulep, &kernend, 0); 275 if (error != 0) { 276 printf("bi_load64 failed: %d\n", error); 277 goto error; 278 } 279 280 mb_mod[0].mod_start = fp->f_addr; 281 mb_mod[0].mod_end = fp->f_addr + fp->f_size - PAGE_SIZE; 282 283 mb_mod[1].mod_start = mb_mod[0].mod_end; 284 mb_mod[1].mod_end = kernend; 285 /* Indicate the kernel metadata is prefixed with a xen_header. */ 286 cmdline = strdup("header"); 287 if (cmdline == NULL) { 288 printf("Out of memory, aborting\n"); 289 error = ENOMEM; 290 goto error; 291 } 292 mb_mod[1].cmdline = VTOP(cmdline); 293 294 mb_info->mods_count = NUM_MODULES; 295 mb_info->mods_addr = VTOP(mb_mod); 296 mb_info->flags |= MULTIBOOT_INFO_MODS; 297 298 header.flags = XENHEADER_HAS_MODULEP_OFFSET; 299 header.modulep_offset = modulep - mb_mod[1].mod_start; 300 archsw.arch_copyin(&header, mb_mod[1].mod_start, sizeof(header)); 301 302 dev_cleanup(); 303 __exec((void *)VTOP(multiboot_tramp), (void *)entry, 304 (void *)VTOP(mb_info)); 305 306 panic("exec returned"); 307 308 error: 309 if (mb_mod) 310 free(mb_mod); 311 if (mb_info) 312 free(mb_info); 313 if (cmdline) 314 free(cmdline); 315 return (error); 316 } 317 318 static int 319 multiboot_obj_loadfile(char *filename, uint64_t dest, 320 struct preloaded_file **result) 321 { 322 struct preloaded_file *mfp, *kfp, *rfp; 323 struct kernel_module *kmp; 324 int error, mod_num; 325 326 /* See if there's a multiboot kernel loaded */ 327 mfp = file_findfile(NULL, "elf multiboot kernel"); 328 if (mfp == NULL) 329 return (EFTYPE); 330 331 /* 332 * We have a multiboot kernel loaded, see if there's a FreeBSD 333 * kernel loaded also. 334 */ 335 kfp = file_findfile(NULL, "elf kernel"); 336 if (kfp == NULL) { 337 /* 338 * No kernel loaded, this must be it. The kernel has to 339 * be loaded as a raw file, it will be processed by 340 * Xen and correctly loaded as an ELF file. 341 */ 342 rfp = file_loadraw(filename, "elf kernel", 0); 343 if (rfp == NULL) { 344 printf( 345 "Unable to load %s as a multiboot payload kernel\n", 346 filename); 347 return (EINVAL); 348 } 349 350 /* Load kernel metadata... */ 351 setenv("kernelname", filename, 1); 352 error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size); 353 if (error) { 354 printf("Unable to load kernel %s metadata error: %d\n", 355 rfp->f_name, error); 356 return (EINVAL); 357 } 358 359 360 /* 361 * Reserve one page at the end of the kernel to place some 362 * metadata in order to cope for Xen relocating the modules and 363 * the metadata information. 364 */ 365 rfp->f_size = roundup(rfp->f_size, PAGE_SIZE); 366 rfp->f_size += PAGE_SIZE; 367 *result = rfp; 368 } else { 369 /* The rest should be loaded as regular modules */ 370 error = elf64_obj_loadfile(filename, dest, result); 371 if (error != 0) { 372 printf("Unable to load %s as an object file, error: %d", 373 filename, error); 374 return (error); 375 } 376 } 377 378 return (0); 379 } 380 381 static int 382 multiboot_obj_exec(struct preloaded_file *fp) 383 { 384 385 return (EFTYPE); 386 } 387