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> 45ca987d46SWarner Losh 46ca987d46SWarner Losh /* 47ca987d46SWarner Losh * The code is excerpted from sys/x86/x86/identcpu.c: identify_cpu(), 48ca987d46SWarner Losh * identify_hypervisor(), and dev/hyperv/vmbus/hyperv.c: hyperv_identify(). 49ca987d46SWarner Losh */ 50ca987d46SWarner Losh #define CPUID_LEAF_HV_MAXLEAF 0x40000000 51ca987d46SWarner Losh #define CPUID_LEAF_HV_INTERFACE 0x40000001 52ca987d46SWarner Losh #define CPUID_LEAF_HV_FEATURES 0x40000003 53ca987d46SWarner Losh #define CPUID_LEAF_HV_LIMITS 0x40000005 54ca987d46SWarner Losh #define CPUID_HV_IFACE_HYPERV 0x31237648 /* HV#1 */ 55ca987d46SWarner Losh #define CPUID_HV_MSR_TIME_REFCNT 0x0002 /* MSR_HV_TIME_REF_COUNT */ 56ca987d46SWarner Losh #define CPUID_HV_MSR_HYPERCALL 0x0020 57ca987d46SWarner Losh 58ca987d46SWarner Losh static int 59ca987d46SWarner Losh running_on_hyperv(void) 60ca987d46SWarner Losh { 61ca987d46SWarner Losh char hv_vendor[16]; 62ca987d46SWarner Losh uint32_t regs[4]; 63ca987d46SWarner Losh 64ca987d46SWarner Losh do_cpuid(1, regs); 65ca987d46SWarner Losh if ((regs[2] & CPUID2_HV) == 0) 66ca987d46SWarner Losh return (0); 67ca987d46SWarner Losh 68ca987d46SWarner Losh do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs); 69ca987d46SWarner Losh if (regs[0] < CPUID_LEAF_HV_LIMITS) 70ca987d46SWarner Losh return (0); 71ca987d46SWarner Losh 72ca987d46SWarner Losh ((uint32_t *)&hv_vendor)[0] = regs[1]; 73ca987d46SWarner Losh ((uint32_t *)&hv_vendor)[1] = regs[2]; 74ca987d46SWarner Losh ((uint32_t *)&hv_vendor)[2] = regs[3]; 75ca987d46SWarner Losh hv_vendor[12] = '\0'; 76ca987d46SWarner Losh if (strcmp(hv_vendor, "Microsoft Hv") != 0) 77ca987d46SWarner Losh return (0); 78ca987d46SWarner Losh 79ca987d46SWarner Losh do_cpuid(CPUID_LEAF_HV_INTERFACE, regs); 80ca987d46SWarner Losh if (regs[0] != CPUID_HV_IFACE_HYPERV) 81ca987d46SWarner Losh return (0); 82ca987d46SWarner Losh 83ca987d46SWarner Losh do_cpuid(CPUID_LEAF_HV_FEATURES, regs); 84ca987d46SWarner Losh if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0) 85ca987d46SWarner Losh return (0); 86ca987d46SWarner Losh if ((regs[0] & CPUID_HV_MSR_TIME_REFCNT) == 0) 87ca987d46SWarner Losh return (0); 88ca987d46SWarner Losh 89ca987d46SWarner Losh return (1); 90ca987d46SWarner Losh } 91ca987d46SWarner Losh 92ca987d46SWarner Losh #define KERNEL_PHYSICAL_BASE (2*1024*1024) 93ca987d46SWarner Losh 94ca987d46SWarner Losh static void 95ca987d46SWarner Losh efi_verify_staging_size(unsigned long *nr_pages) 96ca987d46SWarner Losh { 97ca987d46SWarner Losh UINTN sz; 98ce37b71eSRebecca Cran EFI_MEMORY_DESCRIPTOR *map = NULL, *p; 99ca987d46SWarner Losh EFI_PHYSICAL_ADDRESS start, end; 100ca987d46SWarner Losh UINTN key, dsz; 101ca987d46SWarner Losh UINT32 dver; 102ca987d46SWarner Losh EFI_STATUS status; 103ca987d46SWarner Losh int i, ndesc; 104ca987d46SWarner Losh unsigned long available_pages = 0; 105ca987d46SWarner Losh 106ca987d46SWarner Losh sz = 0; 107ce37b71eSRebecca Cran 108ce37b71eSRebecca Cran for (;;) { 109ce37b71eSRebecca Cran status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); 110ce37b71eSRebecca Cran if (!EFI_ERROR(status)) 111ce37b71eSRebecca Cran break; 112ce37b71eSRebecca Cran 113ca987d46SWarner Losh if (status != EFI_BUFFER_TOO_SMALL) { 114ce37b71eSRebecca Cran printf("Can't read memory map: %lu\n", 115ce37b71eSRebecca Cran EFI_ERROR_CODE(status)); 116ce37b71eSRebecca Cran goto out; 117ca987d46SWarner Losh } 118ca987d46SWarner Losh 119ce37b71eSRebecca Cran free(map); 120ce37b71eSRebecca Cran 121ce37b71eSRebecca Cran /* Allocate 10 descriptors more than the size reported, 122ce37b71eSRebecca Cran * to allow for any fragmentation caused by calling 123ce37b71eSRebecca Cran * malloc */ 124ce37b71eSRebecca Cran map = malloc(sz + (10 * dsz)); 125ce37b71eSRebecca Cran if (map == NULL) { 126ce37b71eSRebecca Cran printf("Unable to allocate memory\n"); 127ca987d46SWarner Losh goto out; 128ca987d46SWarner Losh } 129ce37b71eSRebecca Cran } 130ca987d46SWarner Losh 131ca987d46SWarner Losh ndesc = sz / dsz; 132ca987d46SWarner Losh for (i = 0, p = map; i < ndesc; 133ca987d46SWarner Losh i++, p = NextMemoryDescriptor(p, dsz)) { 134ca987d46SWarner Losh start = p->PhysicalStart; 135ca987d46SWarner Losh end = start + p->NumberOfPages * EFI_PAGE_SIZE; 136ca987d46SWarner Losh 137ca987d46SWarner Losh if (KERNEL_PHYSICAL_BASE < start || 138ca987d46SWarner Losh KERNEL_PHYSICAL_BASE >= end) 139ca987d46SWarner Losh continue; 140ca987d46SWarner Losh 141ca987d46SWarner Losh available_pages = p->NumberOfPages - 142ca987d46SWarner Losh ((KERNEL_PHYSICAL_BASE - start) >> EFI_PAGE_SHIFT); 143ca987d46SWarner Losh break; 144ca987d46SWarner Losh } 145ca987d46SWarner Losh 146ca987d46SWarner Losh if (available_pages == 0) { 147ca987d46SWarner Losh printf("Can't find valid memory map for staging area!\n"); 148ca987d46SWarner Losh goto out; 149ca987d46SWarner Losh } 150ca987d46SWarner Losh 151ca987d46SWarner Losh i++; 152ca987d46SWarner Losh p = NextMemoryDescriptor(p, dsz); 153ca987d46SWarner Losh 154ca987d46SWarner Losh for ( ; i < ndesc; 155ca987d46SWarner Losh i++, p = NextMemoryDescriptor(p, dsz)) { 156ca987d46SWarner Losh if (p->Type != EfiConventionalMemory && 157ca987d46SWarner Losh p->Type != EfiLoaderData) 158ca987d46SWarner Losh break; 159ca987d46SWarner Losh 160ca987d46SWarner Losh if (p->PhysicalStart != end) 161ca987d46SWarner Losh break; 162ca987d46SWarner Losh 163ca987d46SWarner Losh end = p->PhysicalStart + p->NumberOfPages * EFI_PAGE_SIZE; 164ca987d46SWarner Losh 165ca987d46SWarner Losh available_pages += p->NumberOfPages; 166ca987d46SWarner Losh } 167ca987d46SWarner Losh 168ca987d46SWarner Losh if (*nr_pages > available_pages) { 169ca987d46SWarner Losh printf("Staging area's size is reduced: %ld -> %ld!\n", 170ca987d46SWarner Losh *nr_pages, available_pages); 171ca987d46SWarner Losh *nr_pages = available_pages; 172ca987d46SWarner Losh } 173ca987d46SWarner Losh out: 174ca987d46SWarner Losh free(map); 175ca987d46SWarner Losh } 176ca987d46SWarner Losh #endif /* __i386__ || __amd64__ */ 177ca987d46SWarner Losh 178ca987d46SWarner Losh #ifndef EFI_STAGING_SIZE 17994e8f7c6SRebecca Cran #if defined(__amd64__) 18094e8f7c6SRebecca Cran #define EFI_STAGING_SIZE 100 181ec18da7cSTom Jones #elif defined(__arm__) 182ec18da7cSTom Jones #define EFI_STAGING_SIZE 32 18394e8f7c6SRebecca Cran #else 184ca987d46SWarner Losh #define EFI_STAGING_SIZE 64 185ca987d46SWarner Losh #endif 18694e8f7c6SRebecca Cran #endif 187ca987d46SWarner Losh 188e6bb174cSAndrew Turner EFI_PHYSICAL_ADDRESS staging, staging_end, staging_base; 189ca987d46SWarner Losh int stage_offset_set = 0; 190ca987d46SWarner Losh ssize_t stage_offset; 191ca987d46SWarner Losh 192ca987d46SWarner Losh int 193ca987d46SWarner Losh efi_copy_init(void) 194ca987d46SWarner Losh { 195ca987d46SWarner Losh EFI_STATUS status; 196ca987d46SWarner Losh 197ca987d46SWarner Losh unsigned long nr_pages; 198ca987d46SWarner Losh 199ca987d46SWarner Losh nr_pages = EFI_SIZE_TO_PAGES((EFI_STAGING_SIZE) * 1024 * 1024); 200ca987d46SWarner Losh 201ca987d46SWarner Losh #if defined(__i386__) || defined(__amd64__) 202ca987d46SWarner Losh /* 203ca987d46SWarner Losh * We'll decrease nr_pages, if it's too big. Currently we only 204ca987d46SWarner Losh * apply this to FreeBSD VM running on Hyper-V. Why? Please see 205ca987d46SWarner Losh * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211746#c28 206ca987d46SWarner Losh */ 207ca987d46SWarner Losh if (running_on_hyperv()) 208ca987d46SWarner Losh efi_verify_staging_size(&nr_pages); 209ca987d46SWarner Losh 210ca987d46SWarner Losh /* 211ca987d46SWarner Losh * The staging area must reside in the the first 1GB physical 212ca987d46SWarner Losh * memory: see elf64_exec() in 213ca987d46SWarner Losh * boot/efi/loader/arch/amd64/elf64_freebsd.c. 214ca987d46SWarner Losh */ 215ca987d46SWarner Losh staging = 1024*1024*1024; 216ca987d46SWarner Losh status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 217ca987d46SWarner Losh nr_pages, &staging); 218ca987d46SWarner Losh #else 219ca987d46SWarner Losh status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 220ca987d46SWarner Losh nr_pages, &staging); 221ca987d46SWarner Losh #endif 222ca987d46SWarner Losh if (EFI_ERROR(status)) { 223ca987d46SWarner Losh printf("failed to allocate staging area: %lu\n", 224ca987d46SWarner Losh EFI_ERROR_CODE(status)); 225ca987d46SWarner Losh return (status); 226ca987d46SWarner Losh } 227e6bb174cSAndrew Turner staging_base = staging; 228ca987d46SWarner Losh staging_end = staging + nr_pages * EFI_PAGE_SIZE; 229ca987d46SWarner Losh 2302192efc0SMitchell Horne #if defined(__aarch64__) || defined(__arm__) || defined(__riscv) 231ca987d46SWarner Losh /* 232ca987d46SWarner Losh * Round the kernel load address to a 2MiB value. This is needed 233ca987d46SWarner Losh * because the kernel builds a page table based on where it has 234ca987d46SWarner Losh * been loaded in physical address space. As the kernel will use 235ca987d46SWarner Losh * either a 1MiB or 2MiB page for this we need to make sure it 236ca987d46SWarner Losh * is correctly aligned for both cases. 237ca987d46SWarner Losh */ 238ca987d46SWarner Losh staging = roundup2(staging, 2 * 1024 * 1024); 239ca987d46SWarner Losh #endif 240ca987d46SWarner Losh 241ca987d46SWarner Losh return (0); 242ca987d46SWarner Losh } 243ca987d46SWarner Losh 244e6bb174cSAndrew Turner static bool 245e6bb174cSAndrew Turner efi_check_space(vm_offset_t end) 246e6bb174cSAndrew Turner { 247e6bb174cSAndrew Turner EFI_PHYSICAL_ADDRESS addr; 248e6bb174cSAndrew Turner EFI_STATUS status; 249e6bb174cSAndrew Turner unsigned long nr_pages; 250e6bb174cSAndrew Turner 251e6bb174cSAndrew Turner /* There is already enough space */ 252e6bb174cSAndrew Turner if (end <= staging_end) 253e6bb174cSAndrew Turner return (true); 254e6bb174cSAndrew Turner 255e6bb174cSAndrew Turner end = roundup2(end, EFI_PAGE_SIZE); 256e6bb174cSAndrew Turner nr_pages = EFI_SIZE_TO_PAGES(end - staging_end); 257e6bb174cSAndrew Turner 258e6bb174cSAndrew Turner #if defined(__i386__) || defined(__amd64__) 259e6bb174cSAndrew Turner /* X86 needs all memory to be allocated under the 1G boundary */ 260e6bb174cSAndrew Turner if (end > 1024*1024*1024) 261e6bb174cSAndrew Turner goto before_staging; 262e6bb174cSAndrew Turner #endif 263e6bb174cSAndrew Turner 264e6bb174cSAndrew Turner /* Try to allocate more space after the previous allocation */ 265e6bb174cSAndrew Turner addr = staging_end; 266e6bb174cSAndrew Turner status = BS->AllocatePages(AllocateAddress, EfiLoaderData, nr_pages, 267e6bb174cSAndrew Turner &addr); 268e6bb174cSAndrew Turner if (!EFI_ERROR(status)) { 269e6bb174cSAndrew Turner staging_end = staging_end + nr_pages * EFI_PAGE_SIZE; 270e6bb174cSAndrew Turner return (true); 271e6bb174cSAndrew Turner } 272e6bb174cSAndrew Turner 273e6bb174cSAndrew Turner before_staging: 274e6bb174cSAndrew Turner /* Try allocating space before the previous allocation */ 275e6bb174cSAndrew Turner if (staging < nr_pages * EFI_PAGE_SIZE) { 276e6bb174cSAndrew Turner printf("Not enough space before allocation\n"); 277e6bb174cSAndrew Turner return (false); 278e6bb174cSAndrew Turner } 279e6bb174cSAndrew Turner addr = staging - nr_pages * EFI_PAGE_SIZE; 2802192efc0SMitchell Horne #if defined(__aarch64__) || defined(__arm__) || defined(__riscv) 281e6bb174cSAndrew Turner /* See efi_copy_init for why this is needed */ 282e6bb174cSAndrew Turner addr = rounddown2(addr, 2 * 1024 * 1024); 283e6bb174cSAndrew Turner #endif 284e6bb174cSAndrew Turner nr_pages = EFI_SIZE_TO_PAGES(staging_base - addr); 285e6bb174cSAndrew Turner status = BS->AllocatePages(AllocateAddress, EfiLoaderData, nr_pages, 286e6bb174cSAndrew Turner &addr); 287e6bb174cSAndrew Turner if (!EFI_ERROR(status)) { 288e6bb174cSAndrew Turner /* 289e6bb174cSAndrew Turner * Move the old allocation and update the state so 290e6bb174cSAndrew Turner * translation still works. 291e6bb174cSAndrew Turner */ 292e6bb174cSAndrew Turner staging_base = addr; 293*67dc6bedSJohn Baldwin memmove((void *)(uintptr_t)staging_base, 294*67dc6bedSJohn Baldwin (void *)(uintptr_t)staging, staging_end - staging); 295e6bb174cSAndrew Turner stage_offset -= (staging - staging_base); 296e6bb174cSAndrew Turner staging = staging_base; 297e6bb174cSAndrew Turner return (true); 298e6bb174cSAndrew Turner } 299e6bb174cSAndrew Turner 300e6bb174cSAndrew Turner printf("efi_check_space: Unable to expand staging area\n"); 301e6bb174cSAndrew Turner return (false); 302e6bb174cSAndrew Turner } 303e6bb174cSAndrew Turner 304ca987d46SWarner Losh void * 305ca987d46SWarner Losh efi_translate(vm_offset_t ptr) 306ca987d46SWarner Losh { 307ca987d46SWarner Losh 308ca987d46SWarner Losh return ((void *)(ptr + stage_offset)); 309ca987d46SWarner Losh } 310ca987d46SWarner Losh 311ca987d46SWarner Losh ssize_t 312ca987d46SWarner Losh efi_copyin(const void *src, vm_offset_t dest, const size_t len) 313ca987d46SWarner Losh { 314ca987d46SWarner Losh 315ca987d46SWarner Losh if (!stage_offset_set) { 316ca987d46SWarner Losh stage_offset = (vm_offset_t)staging - dest; 317ca987d46SWarner Losh stage_offset_set = 1; 318ca987d46SWarner Losh } 319ca987d46SWarner Losh 320ca987d46SWarner Losh /* XXX: Callers do not check for failure. */ 321e6bb174cSAndrew Turner if (!efi_check_space(dest + stage_offset + len)) { 322ca987d46SWarner Losh errno = ENOMEM; 323ca987d46SWarner Losh return (-1); 324ca987d46SWarner Losh } 325ca987d46SWarner Losh bcopy(src, (void *)(dest + stage_offset), len); 326ca987d46SWarner Losh return (len); 327ca987d46SWarner Losh } 328ca987d46SWarner Losh 329ca987d46SWarner Losh ssize_t 330ca987d46SWarner Losh efi_copyout(const vm_offset_t src, void *dest, const size_t len) 331ca987d46SWarner Losh { 332ca987d46SWarner Losh 333ca987d46SWarner Losh /* XXX: Callers do not check for failure. */ 334ca987d46SWarner Losh if (src + stage_offset + len > staging_end) { 335ca987d46SWarner Losh errno = ENOMEM; 336ca987d46SWarner Losh return (-1); 337ca987d46SWarner Losh } 338ca987d46SWarner Losh bcopy((void *)(src + stage_offset), dest, len); 339ca987d46SWarner Losh return (len); 340ca987d46SWarner Losh } 341ca987d46SWarner Losh 342ca987d46SWarner Losh 343ca987d46SWarner Losh ssize_t 344afc571b1SSimon J. Gerraty efi_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) 345ca987d46SWarner Losh { 346ca987d46SWarner Losh 3472192efc0SMitchell Horne if (!stage_offset_set) { 3482192efc0SMitchell Horne stage_offset = (vm_offset_t)staging - dest; 3492192efc0SMitchell Horne stage_offset_set = 1; 3502192efc0SMitchell Horne } 3512192efc0SMitchell Horne 352e6bb174cSAndrew Turner if (!efi_check_space(dest + stage_offset + len)) { 353ca987d46SWarner Losh errno = ENOMEM; 354ca987d46SWarner Losh return (-1); 355ca987d46SWarner Losh } 356afc571b1SSimon J. Gerraty return (VECTX_READ(fd, (void *)(dest + stage_offset), len)); 357ca987d46SWarner Losh } 358ca987d46SWarner Losh 359ca987d46SWarner Losh void 360ca987d46SWarner Losh efi_copy_finish(void) 361ca987d46SWarner Losh { 362ca987d46SWarner Losh uint64_t *src, *dst, *last; 363ca987d46SWarner Losh 3640b600ec4SJohn Baldwin src = (uint64_t *)(uintptr_t)staging; 3650b600ec4SJohn Baldwin dst = (uint64_t *)(uintptr_t)(staging - stage_offset); 3660b600ec4SJohn Baldwin last = (uint64_t *)(uintptr_t)staging_end; 367ca987d46SWarner Losh 368ca987d46SWarner Losh while (src < last) 369ca987d46SWarner Losh *dst++ = *src++; 370ca987d46SWarner Losh } 371