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