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 179*94e8f7c6SRebecca Cran #if defined(__amd64__) 180*94e8f7c6SRebecca Cran #define EFI_STAGING_SIZE 100 181*94e8f7c6SRebecca Cran #else 182ca987d46SWarner Losh #define EFI_STAGING_SIZE 64 183ca987d46SWarner Losh #endif 184*94e8f7c6SRebecca Cran #endif 185ca987d46SWarner Losh 186ca987d46SWarner Losh EFI_PHYSICAL_ADDRESS staging, staging_end; 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 } 225ca987d46SWarner Losh staging_end = staging + nr_pages * EFI_PAGE_SIZE; 226ca987d46SWarner Losh 227ca987d46SWarner Losh #if defined(__aarch64__) || defined(__arm__) 228ca987d46SWarner Losh /* 229ca987d46SWarner Losh * Round the kernel load address to a 2MiB value. This is needed 230ca987d46SWarner Losh * because the kernel builds a page table based on where it has 231ca987d46SWarner Losh * been loaded in physical address space. As the kernel will use 232ca987d46SWarner Losh * either a 1MiB or 2MiB page for this we need to make sure it 233ca987d46SWarner Losh * is correctly aligned for both cases. 234ca987d46SWarner Losh */ 235ca987d46SWarner Losh staging = roundup2(staging, 2 * 1024 * 1024); 236ca987d46SWarner Losh #endif 237ca987d46SWarner Losh 238ca987d46SWarner Losh return (0); 239ca987d46SWarner Losh } 240ca987d46SWarner Losh 241ca987d46SWarner Losh void * 242ca987d46SWarner Losh efi_translate(vm_offset_t ptr) 243ca987d46SWarner Losh { 244ca987d46SWarner Losh 245ca987d46SWarner Losh return ((void *)(ptr + stage_offset)); 246ca987d46SWarner Losh } 247ca987d46SWarner Losh 248ca987d46SWarner Losh ssize_t 249ca987d46SWarner Losh efi_copyin(const void *src, vm_offset_t dest, const size_t len) 250ca987d46SWarner Losh { 251ca987d46SWarner Losh 252ca987d46SWarner Losh if (!stage_offset_set) { 253ca987d46SWarner Losh stage_offset = (vm_offset_t)staging - dest; 254ca987d46SWarner Losh stage_offset_set = 1; 255ca987d46SWarner Losh } 256ca987d46SWarner Losh 257ca987d46SWarner Losh /* XXX: Callers do not check for failure. */ 258ca987d46SWarner Losh if (dest + stage_offset + len > staging_end) { 259ca987d46SWarner Losh errno = ENOMEM; 260ca987d46SWarner Losh return (-1); 261ca987d46SWarner Losh } 262ca987d46SWarner Losh bcopy(src, (void *)(dest + stage_offset), len); 263ca987d46SWarner Losh return (len); 264ca987d46SWarner Losh } 265ca987d46SWarner Losh 266ca987d46SWarner Losh ssize_t 267ca987d46SWarner Losh efi_copyout(const vm_offset_t src, void *dest, const size_t len) 268ca987d46SWarner Losh { 269ca987d46SWarner Losh 270ca987d46SWarner Losh /* XXX: Callers do not check for failure. */ 271ca987d46SWarner Losh if (src + stage_offset + len > staging_end) { 272ca987d46SWarner Losh errno = ENOMEM; 273ca987d46SWarner Losh return (-1); 274ca987d46SWarner Losh } 275ca987d46SWarner Losh bcopy((void *)(src + stage_offset), dest, len); 276ca987d46SWarner Losh return (len); 277ca987d46SWarner Losh } 278ca987d46SWarner Losh 279ca987d46SWarner Losh 280ca987d46SWarner Losh ssize_t 281ca987d46SWarner Losh efi_readin(const int fd, vm_offset_t dest, const size_t len) 282ca987d46SWarner Losh { 283ca987d46SWarner Losh 284ca987d46SWarner Losh if (dest + stage_offset + len > staging_end) { 285ca987d46SWarner Losh errno = ENOMEM; 286ca987d46SWarner Losh return (-1); 287ca987d46SWarner Losh } 288ca987d46SWarner Losh return (read(fd, (void *)(dest + stage_offset), len)); 289ca987d46SWarner Losh } 290ca987d46SWarner Losh 291ca987d46SWarner Losh void 292ca987d46SWarner Losh efi_copy_finish(void) 293ca987d46SWarner Losh { 294ca987d46SWarner Losh uint64_t *src, *dst, *last; 295ca987d46SWarner Losh 2960b600ec4SJohn Baldwin src = (uint64_t *)(uintptr_t)staging; 2970b600ec4SJohn Baldwin dst = (uint64_t *)(uintptr_t)(staging - stage_offset); 2980b600ec4SJohn Baldwin last = (uint64_t *)(uintptr_t)staging_end; 299ca987d46SWarner Losh 300ca987d46SWarner Losh while (src < last) 301ca987d46SWarner Losh *dst++ = *src++; 302ca987d46SWarner Losh } 303