bootinfo.c revision 344378
1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * Copyright (c) 2004, 2006 Marcel Moolenaar 4 * Copyright (c) 2014 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/11/stand/efi/loader/bootinfo.c 344378 2019-02-20 19:19:24Z kevans $"); 31 32#include <stand.h> 33#include <string.h> 34#include <sys/param.h> 35#include <sys/linker.h> 36#include <sys/reboot.h> 37#include <sys/boot.h> 38#include <machine/cpufunc.h> 39#include <machine/elf.h> 40#include <machine/metadata.h> 41#include <machine/psl.h> 42 43#include <efi.h> 44#include <efilib.h> 45 46#include "bootstrap.h" 47#include "loader_efi.h" 48 49#if defined(__amd64__) 50#include <machine/specialreg.h> 51#endif 52 53#include "framebuffer.h" 54 55#if defined(LOADER_FDT_SUPPORT) 56#include <fdt_platform.h> 57#endif 58 59int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp); 60 61extern EFI_SYSTEM_TABLE *ST; 62 63static int 64bi_getboothowto(char *kargs) 65{ 66 const char *sw; 67 char *opts; 68 char *console; 69 int howto; 70 71 howto = boot_parse_cmdline(kargs); 72 howto |= boot_env_to_howto(); 73 74 console = getenv("console"); 75 if (console != NULL) { 76 if (strcmp(console, "comconsole") == 0) 77 howto |= RB_SERIAL; 78 if (strcmp(console, "nullconsole") == 0) 79 howto |= RB_MUTE; 80 } 81 82 return (howto); 83} 84 85/* 86 * Copy the environment into the load area starting at (addr). 87 * Each variable is formatted as <name>=<value>, with a single nul 88 * separating each variable, and a double nul terminating the environment. 89 */ 90static vm_offset_t 91bi_copyenv(vm_offset_t start) 92{ 93 struct env_var *ep; 94 vm_offset_t addr, last; 95 size_t len; 96 97 addr = last = start; 98 99 /* Traverse the environment. */ 100 for (ep = environ; ep != NULL; ep = ep->ev_next) { 101 len = strlen(ep->ev_name); 102 if ((size_t)archsw.arch_copyin(ep->ev_name, addr, len) != len) 103 break; 104 addr += len; 105 if (archsw.arch_copyin("=", addr, 1) != 1) 106 break; 107 addr++; 108 if (ep->ev_value != NULL) { 109 len = strlen(ep->ev_value); 110 if ((size_t)archsw.arch_copyin(ep->ev_value, addr, len) != len) 111 break; 112 addr += len; 113 } 114 if (archsw.arch_copyin("", addr, 1) != 1) 115 break; 116 last = ++addr; 117 } 118 119 if (archsw.arch_copyin("", last++, 1) != 1) 120 last = start; 121 return(last); 122} 123 124/* 125 * Copy module-related data into the load area, where it can be 126 * used as a directory for loaded modules. 127 * 128 * Module data is presented in a self-describing format. Each datum 129 * is preceded by a 32-bit identifier and a 32-bit size field. 130 * 131 * Currently, the following data are saved: 132 * 133 * MOD_NAME (variable) module name (string) 134 * MOD_TYPE (variable) module type (string) 135 * MOD_ARGS (variable) module parameters (string) 136 * MOD_ADDR sizeof(vm_offset_t) module load address 137 * MOD_SIZE sizeof(size_t) module size 138 * MOD_METADATA (variable) type-specific metadata 139 */ 140#define COPY32(v, a, c) { \ 141 uint32_t x = (v); \ 142 if (c) \ 143 archsw.arch_copyin(&x, a, sizeof(x)); \ 144 a += sizeof(x); \ 145} 146 147#define MOD_STR(t, a, s, c) { \ 148 COPY32(t, a, c); \ 149 COPY32(strlen(s) + 1, a, c); \ 150 if (c) \ 151 archsw.arch_copyin(s, a, strlen(s) + 1); \ 152 a += roundup(strlen(s) + 1, sizeof(u_long)); \ 153} 154 155#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) 156#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) 157#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) 158 159#define MOD_VAR(t, a, s, c) { \ 160 COPY32(t, a, c); \ 161 COPY32(sizeof(s), a, c); \ 162 if (c) \ 163 archsw.arch_copyin(&s, a, sizeof(s)); \ 164 a += roundup(sizeof(s), sizeof(u_long)); \ 165} 166 167#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) 168#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) 169 170#define MOD_METADATA(a, mm, c) { \ 171 COPY32(MODINFO_METADATA | mm->md_type, a, c); \ 172 COPY32(mm->md_size, a, c); \ 173 if (c) \ 174 archsw.arch_copyin(mm->md_data, a, mm->md_size); \ 175 a += roundup(mm->md_size, sizeof(u_long)); \ 176} 177 178#define MOD_END(a, c) { \ 179 COPY32(MODINFO_END, a, c); \ 180 COPY32(0, a, c); \ 181} 182 183static vm_offset_t 184bi_copymodules(vm_offset_t addr) 185{ 186 struct preloaded_file *fp; 187 struct file_metadata *md; 188 int c; 189 uint64_t v; 190 191 c = addr != 0; 192 /* Start with the first module on the list, should be the kernel. */ 193 for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { 194 MOD_NAME(addr, fp->f_name, c); /* This must come first. */ 195 MOD_TYPE(addr, fp->f_type, c); 196 if (fp->f_args) 197 MOD_ARGS(addr, fp->f_args, c); 198 v = fp->f_addr; 199#if defined(__arm__) 200 v -= __elfN(relocation_offset); 201#endif 202 MOD_ADDR(addr, v, c); 203 v = fp->f_size; 204 MOD_SIZE(addr, v, c); 205 for (md = fp->f_metadata; md != NULL; md = md->md_next) 206 if (!(md->md_type & MODINFOMD_NOCOPY)) 207 MOD_METADATA(addr, md, c); 208 } 209 MOD_END(addr, c); 210 return(addr); 211} 212 213static EFI_STATUS 214efi_do_vmap(EFI_MEMORY_DESCRIPTOR *mm, UINTN sz, UINTN mmsz, UINT32 mmver) 215{ 216 EFI_MEMORY_DESCRIPTOR *desc, *viter, *vmap; 217 EFI_STATUS ret; 218 int curr, ndesc, nset; 219 220 nset = 0; 221 desc = mm; 222 ndesc = sz / mmsz; 223 vmap = malloc(sz); 224 if (vmap == NULL) 225 /* This isn't really an EFI error case, but pretend it is */ 226 return (EFI_OUT_OF_RESOURCES); 227 viter = vmap; 228 for (curr = 0; curr < ndesc; 229 curr++, desc = NextMemoryDescriptor(desc, mmsz)) { 230 if ((desc->Attribute & EFI_MEMORY_RUNTIME) != 0) { 231 ++nset; 232 desc->VirtualStart = desc->PhysicalStart; 233 *viter = *desc; 234 viter = NextMemoryDescriptor(viter, mmsz); 235 } 236 } 237 ret = RS->SetVirtualAddressMap(nset * mmsz, mmsz, mmver, vmap); 238 free(vmap); 239 return (ret); 240} 241 242static int 243bi_load_efi_data(struct preloaded_file *kfp) 244{ 245 EFI_MEMORY_DESCRIPTOR *mm; 246 EFI_PHYSICAL_ADDRESS addr; 247 EFI_STATUS status; 248 const char *efi_novmap; 249 size_t efisz; 250 UINTN efi_mapkey; 251 UINTN mmsz, pages, retry, sz; 252 UINT32 mmver; 253 struct efi_map_header *efihdr; 254 bool do_vmap; 255 256#if defined(__amd64__) || defined(__aarch64__) 257 struct efi_fb efifb; 258 259 if (efi_find_framebuffer(&efifb) == 0) { 260 printf("EFI framebuffer information:\n"); 261 printf("addr, size 0x%jx, 0x%jx\n", efifb.fb_addr, 262 efifb.fb_size); 263 printf("dimensions %d x %d\n", efifb.fb_width, 264 efifb.fb_height); 265 printf("stride %d\n", efifb.fb_stride); 266 printf("masks 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", 267 efifb.fb_mask_red, efifb.fb_mask_green, efifb.fb_mask_blue, 268 efifb.fb_mask_reserved); 269 270 file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb); 271 } 272#endif 273 274 do_vmap = true; 275 efi_novmap = getenv("efi_disable_vmap"); 276 if (efi_novmap != NULL) 277 do_vmap = strcasecmp(efi_novmap, "YES") != 0; 278 279 efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; 280 281 /* 282 * Assgin size of EFI_MEMORY_DESCRIPTOR to keep compatible with 283 * u-boot which doesn't fill this value when buffer for memory 284 * descriptors is too small (eg. 0 to obtain memory map size) 285 */ 286 mmsz = sizeof(EFI_MEMORY_DESCRIPTOR); 287 288 /* 289 * It is possible that the first call to ExitBootServices may change 290 * the map key. Fetch a new map key and retry ExitBootServices in that 291 * case. 292 */ 293 for (retry = 2; retry > 0; retry--) { 294 /* 295 * Allocate enough pages to hold the bootinfo block and the 296 * memory map EFI will return to us. The memory map has an 297 * unknown size, so we have to determine that first. Note that 298 * the AllocatePages call can itself modify the memory map, so 299 * we have to take that into account as well. The changes to 300 * the memory map are caused by splitting a range of free 301 * memory into two (AFAICT), so that one is marked as being 302 * loader data. 303 */ 304 sz = 0; 305 BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver); 306 sz += mmsz; 307 sz = (sz + 0xf) & ~0xf; 308 pages = EFI_SIZE_TO_PAGES(sz + efisz); 309 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 310 pages, &addr); 311 if (EFI_ERROR(status)) { 312 printf("%s: AllocatePages error %lu\n", __func__, 313 EFI_ERROR_CODE(status)); 314 return (ENOMEM); 315 } 316 317 /* 318 * Read the memory map and stash it after bootinfo. Align the 319 * memory map on a 16-byte boundary (the bootinfo block is page 320 * aligned). 321 */ 322 efihdr = (struct efi_map_header *)(uintptr_t)addr; 323 mm = (void *)((uint8_t *)efihdr + efisz); 324 sz = (EFI_PAGE_SIZE * pages) - efisz; 325 326 status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver); 327 if (EFI_ERROR(status)) { 328 printf("%s: GetMemoryMap error %lu\n", __func__, 329 EFI_ERROR_CODE(status)); 330 return (EINVAL); 331 } 332 status = BS->ExitBootServices(IH, efi_mapkey); 333 if (EFI_ERROR(status) == 0) { 334 /* 335 * This may be disabled by setting efi_disable_vmap in 336 * loader.conf(5). By default we will setup the virtual 337 * map entries. 338 */ 339 if (do_vmap) 340 efi_do_vmap(mm, sz, mmsz, mmver); 341 efihdr->memory_size = sz; 342 efihdr->descriptor_size = mmsz; 343 efihdr->descriptor_version = mmver; 344 file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, 345 efihdr); 346 return (0); 347 } 348 BS->FreePages(addr, pages); 349 } 350 printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status)); 351 return (EINVAL); 352} 353 354/* 355 * Load the information expected by an amd64 kernel. 356 * 357 * - The 'boothowto' argument is constructed. 358 * - The 'bootdev' argument is constructed. 359 * - The 'bootinfo' struct is constructed, and copied into the kernel space. 360 * - The kernel environment is copied into kernel space. 361 * - Module metadata are formatted and placed in kernel space. 362 */ 363int 364bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp) 365{ 366 struct preloaded_file *xp, *kfp; 367 struct devdesc *rootdev; 368 struct file_metadata *md; 369 vm_offset_t addr; 370 uint64_t kernend; 371 uint64_t envp; 372 vm_offset_t size; 373 char *rootdevname; 374 int howto; 375#if defined(LOADER_FDT_SUPPORT) 376 vm_offset_t dtbp; 377 int dtb_size; 378#endif 379#if defined(__arm__) 380 vm_offset_t vaddr; 381 size_t i; 382 /* 383 * These metadata addreses must be converted for kernel after 384 * relocation. 385 */ 386 uint32_t mdt[] = { 387 MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND, 388 MODINFOMD_ENVP, 389#if defined(LOADER_FDT_SUPPORT) 390 MODINFOMD_DTBP 391#endif 392 }; 393#endif 394 395 howto = bi_getboothowto(args); 396 397 /* 398 * Allow the environment variable 'rootdev' to override the supplied 399 * device. This should perhaps go to MI code and/or have $rootdev 400 * tested/set by MI code before launching the kernel. 401 */ 402 rootdevname = getenv("rootdev"); 403 archsw.arch_getdev((void**)(&rootdev), rootdevname, NULL); 404 if (rootdev == NULL) { 405 printf("Can't determine root device.\n"); 406 return(EINVAL); 407 } 408 409 /* Try reading the /etc/fstab file to select the root device */ 410 getrootmount(efi_fmtdev((void *)rootdev)); 411 412 addr = 0; 413 for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { 414 if (addr < (xp->f_addr + xp->f_size)) 415 addr = xp->f_addr + xp->f_size; 416 } 417 418 /* Pad to a page boundary. */ 419 addr = roundup(addr, PAGE_SIZE); 420 421 /* Copy our environment. */ 422 envp = addr; 423 addr = bi_copyenv(addr); 424 425 /* Pad to a page boundary. */ 426 addr = roundup(addr, PAGE_SIZE); 427 428#if defined(LOADER_FDT_SUPPORT) 429 /* Handle device tree blob */ 430 dtbp = addr; 431 dtb_size = fdt_copy(addr); 432 433 /* Pad to a page boundary */ 434 if (dtb_size) 435 addr += roundup(dtb_size, PAGE_SIZE); 436#endif 437 438 kfp = file_findfile(NULL, "elf kernel"); 439 if (kfp == NULL) 440 kfp = file_findfile(NULL, "elf64 kernel"); 441 if (kfp == NULL) 442 panic("can't find kernel file"); 443 kernend = 0; /* fill it in later */ 444 file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); 445 file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); 446#if defined(LOADER_FDT_SUPPORT) 447 if (dtb_size) 448 file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp); 449 else 450 printf("WARNING! Trying to fire up the kernel, but no " 451 "device tree blob found!\n"); 452#endif 453 file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); 454 file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST); 455 456 bi_load_efi_data(kfp); 457 458 /* Figure out the size and location of the metadata. */ 459 *modulep = addr; 460 size = bi_copymodules(0); 461 kernend = roundup(addr + size, PAGE_SIZE); 462 *kernendp = kernend; 463 464 /* patch MODINFOMD_KERNEND */ 465 md = file_findmetadata(kfp, MODINFOMD_KERNEND); 466 bcopy(&kernend, md->md_data, sizeof kernend); 467 468#if defined(__arm__) 469 *modulep -= __elfN(relocation_offset); 470 471 /* Do relocation fixup on metadata of each module. */ 472 for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { 473 for (i = 0; i < nitems(mdt); i++) { 474 md = file_findmetadata(xp, mdt[i]); 475 if (md) { 476 bcopy(md->md_data, &vaddr, sizeof vaddr); 477 vaddr -= __elfN(relocation_offset); 478 bcopy(&vaddr, md->md_data, sizeof vaddr); 479 } 480 } 481 } 482#endif 483 484 /* Copy module list and metadata. */ 485 (void)bi_copymodules(addr); 486 487 return (0); 488} 489