1ca987d46SWarner Losh /*- 2ca987d46SWarner Losh * Copyright (c) 2013 The FreeBSD Foundation 3ca987d46SWarner Losh * All rights reserved. 4ca987d46SWarner Losh * 5ca987d46SWarner Losh * This software was developed by Benno Rice under sponsorship from 6ca987d46SWarner Losh * the FreeBSD Foundation. 7ca987d46SWarner Losh * Redistribution and use in source and binary forms, with or without 8ca987d46SWarner Losh * modification, are permitted provided that the following conditions 9ca987d46SWarner Losh * are met: 10ca987d46SWarner Losh * 1. Redistributions of source code must retain the above copyright 11ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer. 12ca987d46SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 13ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer in the 14ca987d46SWarner Losh * documentation and/or other materials provided with the distribution. 15ca987d46SWarner Losh * 16ca987d46SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17ca987d46SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18ca987d46SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19ca987d46SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20ca987d46SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21ca987d46SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22ca987d46SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23ca987d46SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24ca987d46SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25ca987d46SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26ca987d46SWarner Losh * SUCH DAMAGE. 27ca987d46SWarner Losh */ 28ca987d46SWarner Losh 29ca987d46SWarner Losh #include <sys/cdefs.h> 30ca987d46SWarner Losh __FBSDID("$FreeBSD$"); 31ca987d46SWarner Losh 32ca987d46SWarner Losh #include <sys/param.h> 33ca987d46SWarner Losh 34ca987d46SWarner Losh #include <stand.h> 35ca987d46SWarner Losh #include <bootstrap.h> 36ca987d46SWarner Losh 37ca987d46SWarner Losh #include <efi.h> 38ca987d46SWarner Losh #include <efilib.h> 39ca987d46SWarner Losh 40ca987d46SWarner Losh #include "loader_efi.h" 41ca987d46SWarner Losh 42ca987d46SWarner Losh #if defined(__i386__) || defined(__amd64__) 43ca987d46SWarner Losh #include <machine/cpufunc.h> 44ca987d46SWarner Losh #include <machine/specialreg.h> 45*fd2ef8efSMaxim Sobolev #include <machine/vmparam.h> 46ca987d46SWarner Losh 47ca987d46SWarner Losh /* 48ca987d46SWarner Losh * The code is excerpted from sys/x86/x86/identcpu.c: identify_cpu(), 49ca987d46SWarner Losh * identify_hypervisor(), and dev/hyperv/vmbus/hyperv.c: hyperv_identify(). 50ca987d46SWarner Losh */ 51ca987d46SWarner Losh #define CPUID_LEAF_HV_MAXLEAF 0x40000000 52ca987d46SWarner Losh #define CPUID_LEAF_HV_INTERFACE 0x40000001 53ca987d46SWarner Losh #define CPUID_LEAF_HV_FEATURES 0x40000003 54ca987d46SWarner Losh #define CPUID_LEAF_HV_LIMITS 0x40000005 55ca987d46SWarner Losh #define CPUID_HV_IFACE_HYPERV 0x31237648 /* HV#1 */ 56ca987d46SWarner Losh #define CPUID_HV_MSR_TIME_REFCNT 0x0002 /* MSR_HV_TIME_REF_COUNT */ 57ca987d46SWarner Losh #define CPUID_HV_MSR_HYPERCALL 0x0020 58ca987d46SWarner Losh 59ca987d46SWarner Losh static int 60ca987d46SWarner Losh running_on_hyperv(void) 61ca987d46SWarner Losh { 62ca987d46SWarner Losh char hv_vendor[16]; 63ca987d46SWarner Losh uint32_t regs[4]; 64ca987d46SWarner Losh 65ca987d46SWarner Losh do_cpuid(1, regs); 66ca987d46SWarner Losh if ((regs[2] & CPUID2_HV) == 0) 67ca987d46SWarner Losh return (0); 68ca987d46SWarner Losh 69ca987d46SWarner Losh do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs); 70ca987d46SWarner Losh if (regs[0] < CPUID_LEAF_HV_LIMITS) 71ca987d46SWarner Losh return (0); 72ca987d46SWarner Losh 73ca987d46SWarner Losh ((uint32_t *)&hv_vendor)[0] = regs[1]; 74ca987d46SWarner Losh ((uint32_t *)&hv_vendor)[1] = regs[2]; 75ca987d46SWarner Losh ((uint32_t *)&hv_vendor)[2] = regs[3]; 76ca987d46SWarner Losh hv_vendor[12] = '\0'; 77ca987d46SWarner Losh if (strcmp(hv_vendor, "Microsoft Hv") != 0) 78ca987d46SWarner Losh return (0); 79ca987d46SWarner Losh 80ca987d46SWarner Losh do_cpuid(CPUID_LEAF_HV_INTERFACE, regs); 81ca987d46SWarner Losh if (regs[0] != CPUID_HV_IFACE_HYPERV) 82ca987d46SWarner Losh return (0); 83ca987d46SWarner Losh 84ca987d46SWarner Losh do_cpuid(CPUID_LEAF_HV_FEATURES, regs); 85ca987d46SWarner Losh if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0) 86ca987d46SWarner Losh return (0); 87ca987d46SWarner Losh if ((regs[0] & CPUID_HV_MSR_TIME_REFCNT) == 0) 88ca987d46SWarner Losh return (0); 89ca987d46SWarner Losh 90ca987d46SWarner Losh return (1); 91ca987d46SWarner Losh } 92ca987d46SWarner Losh 93ca987d46SWarner Losh static void 94ca987d46SWarner Losh efi_verify_staging_size(unsigned long *nr_pages) 95ca987d46SWarner Losh { 96ca987d46SWarner Losh UINTN sz; 97ce37b71eSRebecca Cran EFI_MEMORY_DESCRIPTOR *map = NULL, *p; 98ca987d46SWarner Losh EFI_PHYSICAL_ADDRESS start, end; 99ca987d46SWarner Losh UINTN key, dsz; 100ca987d46SWarner Losh UINT32 dver; 101ca987d46SWarner Losh EFI_STATUS status; 102ca987d46SWarner Losh int i, ndesc; 103ca987d46SWarner Losh unsigned long available_pages = 0; 104ca987d46SWarner Losh 105ca987d46SWarner Losh sz = 0; 106ce37b71eSRebecca Cran 107ce37b71eSRebecca Cran for (;;) { 108ce37b71eSRebecca Cran status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); 109ce37b71eSRebecca Cran if (!EFI_ERROR(status)) 110ce37b71eSRebecca Cran break; 111ce37b71eSRebecca Cran 112ca987d46SWarner Losh if (status != EFI_BUFFER_TOO_SMALL) { 113ce37b71eSRebecca Cran printf("Can't read memory map: %lu\n", 114ce37b71eSRebecca Cran EFI_ERROR_CODE(status)); 115ce37b71eSRebecca Cran goto out; 116ca987d46SWarner Losh } 117ca987d46SWarner Losh 118ce37b71eSRebecca Cran free(map); 119ce37b71eSRebecca Cran 120ce37b71eSRebecca Cran /* Allocate 10 descriptors more than the size reported, 121ce37b71eSRebecca Cran * to allow for any fragmentation caused by calling 122ce37b71eSRebecca Cran * malloc */ 123ce37b71eSRebecca Cran map = malloc(sz + (10 * dsz)); 124ce37b71eSRebecca Cran if (map == NULL) { 125ce37b71eSRebecca Cran printf("Unable to allocate memory\n"); 126ca987d46SWarner Losh goto out; 127ca987d46SWarner Losh } 128ce37b71eSRebecca Cran } 129ca987d46SWarner Losh 130ca987d46SWarner Losh ndesc = sz / dsz; 131ca987d46SWarner Losh for (i = 0, p = map; i < ndesc; 132ca987d46SWarner Losh i++, p = NextMemoryDescriptor(p, dsz)) { 133ca987d46SWarner Losh start = p->PhysicalStart; 134ca987d46SWarner Losh end = start + p->NumberOfPages * EFI_PAGE_SIZE; 135ca987d46SWarner Losh 136*fd2ef8efSMaxim Sobolev if (KERNLOAD < start || KERNLOAD >= end) 137ca987d46SWarner Losh continue; 138ca987d46SWarner Losh 139ca987d46SWarner Losh available_pages = p->NumberOfPages - 140*fd2ef8efSMaxim Sobolev ((KERNLOAD - start) >> EFI_PAGE_SHIFT); 141ca987d46SWarner Losh break; 142ca987d46SWarner Losh } 143ca987d46SWarner Losh 144ca987d46SWarner Losh if (available_pages == 0) { 145ca987d46SWarner Losh printf("Can't find valid memory map for staging area!\n"); 146ca987d46SWarner Losh goto out; 147ca987d46SWarner Losh } 148ca987d46SWarner Losh 149ca987d46SWarner Losh i++; 150ca987d46SWarner Losh p = NextMemoryDescriptor(p, dsz); 151ca987d46SWarner Losh 152ca987d46SWarner Losh for ( ; i < ndesc; 153ca987d46SWarner Losh i++, p = NextMemoryDescriptor(p, dsz)) { 154ca987d46SWarner Losh if (p->Type != EfiConventionalMemory && 155ca987d46SWarner Losh p->Type != EfiLoaderData) 156ca987d46SWarner Losh break; 157ca987d46SWarner Losh 158ca987d46SWarner Losh if (p->PhysicalStart != end) 159ca987d46SWarner Losh break; 160ca987d46SWarner Losh 161ca987d46SWarner Losh end = p->PhysicalStart + p->NumberOfPages * EFI_PAGE_SIZE; 162ca987d46SWarner Losh 163ca987d46SWarner Losh available_pages += p->NumberOfPages; 164ca987d46SWarner Losh } 165ca987d46SWarner Losh 166ca987d46SWarner Losh if (*nr_pages > available_pages) { 167ca987d46SWarner Losh printf("Staging area's size is reduced: %ld -> %ld!\n", 168ca987d46SWarner Losh *nr_pages, available_pages); 169ca987d46SWarner Losh *nr_pages = available_pages; 170ca987d46SWarner Losh } 171ca987d46SWarner Losh out: 172ca987d46SWarner Losh free(map); 173ca987d46SWarner Losh } 174ca987d46SWarner Losh #endif /* __i386__ || __amd64__ */ 175ca987d46SWarner Losh 176ca987d46SWarner Losh #ifndef EFI_STAGING_SIZE 17794e8f7c6SRebecca Cran #if defined(__amd64__) 17894e8f7c6SRebecca Cran #define EFI_STAGING_SIZE 100 179ec18da7cSTom Jones #elif defined(__arm__) 180ec18da7cSTom Jones #define EFI_STAGING_SIZE 32 18194e8f7c6SRebecca Cran #else 182ca987d46SWarner Losh #define EFI_STAGING_SIZE 64 183ca987d46SWarner Losh #endif 18494e8f7c6SRebecca Cran #endif 185ca987d46SWarner Losh 186e6bb174cSAndrew Turner EFI_PHYSICAL_ADDRESS staging, staging_end, staging_base; 187ca987d46SWarner Losh int stage_offset_set = 0; 188ca987d46SWarner Losh ssize_t stage_offset; 189ca987d46SWarner Losh 190ca987d46SWarner Losh int 191ca987d46SWarner Losh efi_copy_init(void) 192ca987d46SWarner Losh { 193ca987d46SWarner Losh EFI_STATUS status; 194ca987d46SWarner Losh 195ca987d46SWarner Losh unsigned long nr_pages; 196ca987d46SWarner Losh 197ca987d46SWarner Losh nr_pages = EFI_SIZE_TO_PAGES((EFI_STAGING_SIZE) * 1024 * 1024); 198ca987d46SWarner Losh 199ca987d46SWarner Losh #if defined(__i386__) || defined(__amd64__) 200ca987d46SWarner Losh /* 201ca987d46SWarner Losh * We'll decrease nr_pages, if it's too big. Currently we only 202ca987d46SWarner Losh * apply this to FreeBSD VM running on Hyper-V. Why? Please see 203ca987d46SWarner Losh * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211746#c28 204ca987d46SWarner Losh */ 205ca987d46SWarner Losh if (running_on_hyperv()) 206ca987d46SWarner Losh efi_verify_staging_size(&nr_pages); 207ca987d46SWarner Losh 208ca987d46SWarner Losh /* 209ca987d46SWarner Losh * The staging area must reside in the the first 1GB physical 210ca987d46SWarner Losh * memory: see elf64_exec() in 211ca987d46SWarner Losh * boot/efi/loader/arch/amd64/elf64_freebsd.c. 212ca987d46SWarner Losh */ 213ca987d46SWarner Losh staging = 1024*1024*1024; 214ca987d46SWarner Losh status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 215ca987d46SWarner Losh nr_pages, &staging); 216ca987d46SWarner Losh #else 217ca987d46SWarner Losh status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 218ca987d46SWarner Losh nr_pages, &staging); 219ca987d46SWarner Losh #endif 220ca987d46SWarner Losh if (EFI_ERROR(status)) { 221ca987d46SWarner Losh printf("failed to allocate staging area: %lu\n", 222ca987d46SWarner Losh EFI_ERROR_CODE(status)); 223ca987d46SWarner Losh return (status); 224ca987d46SWarner Losh } 225e6bb174cSAndrew Turner staging_base = staging; 226ca987d46SWarner Losh staging_end = staging + nr_pages * EFI_PAGE_SIZE; 227ca987d46SWarner Losh 2282192efc0SMitchell Horne #if defined(__aarch64__) || defined(__arm__) || defined(__riscv) 229ca987d46SWarner Losh /* 230ca987d46SWarner Losh * Round the kernel load address to a 2MiB value. This is needed 231ca987d46SWarner Losh * because the kernel builds a page table based on where it has 232ca987d46SWarner Losh * been loaded in physical address space. As the kernel will use 233ca987d46SWarner Losh * either a 1MiB or 2MiB page for this we need to make sure it 234ca987d46SWarner Losh * is correctly aligned for both cases. 235ca987d46SWarner Losh */ 236ca987d46SWarner Losh staging = roundup2(staging, 2 * 1024 * 1024); 237ca987d46SWarner Losh #endif 238ca987d46SWarner Losh 239ca987d46SWarner Losh return (0); 240ca987d46SWarner Losh } 241ca987d46SWarner Losh 242e6bb174cSAndrew Turner static bool 243e6bb174cSAndrew Turner efi_check_space(vm_offset_t end) 244e6bb174cSAndrew Turner { 245e6bb174cSAndrew Turner EFI_PHYSICAL_ADDRESS addr; 246e6bb174cSAndrew Turner EFI_STATUS status; 247e6bb174cSAndrew Turner unsigned long nr_pages; 248e6bb174cSAndrew Turner 249e6bb174cSAndrew Turner /* There is already enough space */ 250e6bb174cSAndrew Turner if (end <= staging_end) 251e6bb174cSAndrew Turner return (true); 252e6bb174cSAndrew Turner 253e6bb174cSAndrew Turner end = roundup2(end, EFI_PAGE_SIZE); 254e6bb174cSAndrew Turner nr_pages = EFI_SIZE_TO_PAGES(end - staging_end); 255e6bb174cSAndrew Turner 256e6bb174cSAndrew Turner #if defined(__i386__) || defined(__amd64__) 257e6bb174cSAndrew Turner /* X86 needs all memory to be allocated under the 1G boundary */ 258e6bb174cSAndrew Turner if (end > 1024*1024*1024) 259e6bb174cSAndrew Turner goto before_staging; 260e6bb174cSAndrew Turner #endif 261e6bb174cSAndrew Turner 262e6bb174cSAndrew Turner /* Try to allocate more space after the previous allocation */ 263e6bb174cSAndrew Turner addr = staging_end; 264e6bb174cSAndrew Turner status = BS->AllocatePages(AllocateAddress, EfiLoaderData, nr_pages, 265e6bb174cSAndrew Turner &addr); 266e6bb174cSAndrew Turner if (!EFI_ERROR(status)) { 267e6bb174cSAndrew Turner staging_end = staging_end + nr_pages * EFI_PAGE_SIZE; 268e6bb174cSAndrew Turner return (true); 269e6bb174cSAndrew Turner } 270e6bb174cSAndrew Turner 271e6bb174cSAndrew Turner before_staging: 272e6bb174cSAndrew Turner /* Try allocating space before the previous allocation */ 273e6bb174cSAndrew Turner if (staging < nr_pages * EFI_PAGE_SIZE) { 274e6bb174cSAndrew Turner printf("Not enough space before allocation\n"); 275e6bb174cSAndrew Turner return (false); 276e6bb174cSAndrew Turner } 277e6bb174cSAndrew Turner addr = staging - nr_pages * EFI_PAGE_SIZE; 2782192efc0SMitchell Horne #if defined(__aarch64__) || defined(__arm__) || defined(__riscv) 279e6bb174cSAndrew Turner /* See efi_copy_init for why this is needed */ 280e6bb174cSAndrew Turner addr = rounddown2(addr, 2 * 1024 * 1024); 281e6bb174cSAndrew Turner #endif 282e6bb174cSAndrew Turner nr_pages = EFI_SIZE_TO_PAGES(staging_base - addr); 283e6bb174cSAndrew Turner status = BS->AllocatePages(AllocateAddress, EfiLoaderData, nr_pages, 284e6bb174cSAndrew Turner &addr); 285e6bb174cSAndrew Turner if (!EFI_ERROR(status)) { 286e6bb174cSAndrew Turner /* 287e6bb174cSAndrew Turner * Move the old allocation and update the state so 288e6bb174cSAndrew Turner * translation still works. 289e6bb174cSAndrew Turner */ 290e6bb174cSAndrew Turner staging_base = addr; 29167dc6bedSJohn Baldwin memmove((void *)(uintptr_t)staging_base, 29267dc6bedSJohn Baldwin (void *)(uintptr_t)staging, staging_end - staging); 293e6bb174cSAndrew Turner stage_offset -= (staging - staging_base); 294e6bb174cSAndrew Turner staging = staging_base; 295e6bb174cSAndrew Turner return (true); 296e6bb174cSAndrew Turner } 297e6bb174cSAndrew Turner 298e6bb174cSAndrew Turner printf("efi_check_space: Unable to expand staging area\n"); 299e6bb174cSAndrew Turner return (false); 300e6bb174cSAndrew Turner } 301e6bb174cSAndrew Turner 302ca987d46SWarner Losh void * 303ca987d46SWarner Losh efi_translate(vm_offset_t ptr) 304ca987d46SWarner Losh { 305ca987d46SWarner Losh 306ca987d46SWarner Losh return ((void *)(ptr + stage_offset)); 307ca987d46SWarner Losh } 308ca987d46SWarner Losh 309ca987d46SWarner Losh ssize_t 310ca987d46SWarner Losh efi_copyin(const void *src, vm_offset_t dest, const size_t len) 311ca987d46SWarner Losh { 312ca987d46SWarner Losh 313ca987d46SWarner Losh if (!stage_offset_set) { 314ca987d46SWarner Losh stage_offset = (vm_offset_t)staging - dest; 315ca987d46SWarner Losh stage_offset_set = 1; 316ca987d46SWarner Losh } 317ca987d46SWarner Losh 318ca987d46SWarner Losh /* XXX: Callers do not check for failure. */ 319e6bb174cSAndrew Turner if (!efi_check_space(dest + stage_offset + len)) { 320ca987d46SWarner Losh errno = ENOMEM; 321ca987d46SWarner Losh return (-1); 322ca987d46SWarner Losh } 323ca987d46SWarner Losh bcopy(src, (void *)(dest + stage_offset), len); 324ca987d46SWarner Losh return (len); 325ca987d46SWarner Losh } 326ca987d46SWarner Losh 327ca987d46SWarner Losh ssize_t 328ca987d46SWarner Losh efi_copyout(const vm_offset_t src, void *dest, const size_t len) 329ca987d46SWarner Losh { 330ca987d46SWarner Losh 331ca987d46SWarner Losh /* XXX: Callers do not check for failure. */ 332ca987d46SWarner Losh if (src + stage_offset + len > staging_end) { 333ca987d46SWarner Losh errno = ENOMEM; 334ca987d46SWarner Losh return (-1); 335ca987d46SWarner Losh } 336ca987d46SWarner Losh bcopy((void *)(src + stage_offset), dest, len); 337ca987d46SWarner Losh return (len); 338ca987d46SWarner Losh } 339ca987d46SWarner Losh 340ca987d46SWarner Losh 341ca987d46SWarner Losh ssize_t 342afc571b1SSimon J. Gerraty efi_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) 343ca987d46SWarner Losh { 344ca987d46SWarner Losh 3452192efc0SMitchell Horne if (!stage_offset_set) { 3462192efc0SMitchell Horne stage_offset = (vm_offset_t)staging - dest; 3472192efc0SMitchell Horne stage_offset_set = 1; 3482192efc0SMitchell Horne } 3492192efc0SMitchell Horne 350e6bb174cSAndrew Turner if (!efi_check_space(dest + stage_offset + len)) { 351ca987d46SWarner Losh errno = ENOMEM; 352ca987d46SWarner Losh return (-1); 353ca987d46SWarner Losh } 354afc571b1SSimon J. Gerraty return (VECTX_READ(fd, (void *)(dest + stage_offset), len)); 355ca987d46SWarner Losh } 356ca987d46SWarner Losh 357ca987d46SWarner Losh void 358ca987d46SWarner Losh efi_copy_finish(void) 359ca987d46SWarner Losh { 360ca987d46SWarner Losh uint64_t *src, *dst, *last; 361ca987d46SWarner Losh 3620b600ec4SJohn Baldwin src = (uint64_t *)(uintptr_t)staging; 3630b600ec4SJohn Baldwin dst = (uint64_t *)(uintptr_t)(staging - stage_offset); 3640b600ec4SJohn Baldwin last = (uint64_t *)(uintptr_t)staging_end; 365ca987d46SWarner Losh 366ca987d46SWarner Losh while (src < last) 367ca987d46SWarner Losh *dst++ = *src++; 368ca987d46SWarner Losh } 369