1 /* 2 * Copyright (c) 2013 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Benno Rice under sponsorship from 6 * the FreeBSD Foundation. 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 31 #include <sys/param.h> 32 #include <sys/multiboot2.h> 33 34 #include <stand.h> 35 #include <bootstrap.h> 36 37 #include <efi.h> 38 #include <efilib.h> 39 40 #include "loader_efi.h" 41 42 /* 43 * Verify the address is not in use by existing modules. 44 */ 45 static vm_offset_t 46 addr_verify(multiboot_tag_module_t *module, vm_offset_t addr, size_t size) 47 { 48 vm_offset_t start, end; 49 50 for (; module->mb_type == MULTIBOOT_TAG_TYPE_MODULE; 51 module = (multiboot_tag_module_t *) 52 roundup((uintptr_t)module + module->mb_size, MULTIBOOT_TAG_ALIGN)) { 53 54 start = module->mb_mod_start; 55 end = module->mb_mod_end; 56 57 /* Does this module have address assigned? */ 58 if (start == 0) 59 continue; 60 61 if ((start <= addr) && (end >= addr)) { 62 return (0); 63 } 64 if ((start >= addr) && (start <= addr + size)) { 65 return (0); 66 } 67 } 68 return (addr); 69 } 70 71 /* 72 * Find memory map entry above 1MB, able to contain size bytes from addr. 73 */ 74 static vm_offset_t 75 memmap_find(EFI_MEMORY_DESCRIPTOR *map, size_t count, UINTN dsize, 76 vm_offset_t addr, size_t size) 77 { 78 int i; 79 80 for (i = 0; i < count; i++, map = NextMemoryDescriptor(map, dsize)) { 81 82 if (map->Type != EfiConventionalMemory) 83 continue; 84 85 /* We do not want address below 1MB. */ 86 if (map->PhysicalStart < 0x100000) 87 continue; 88 89 /* Do we fit into current entry? */ 90 if ((map->PhysicalStart <= addr) && 91 (map->PhysicalStart + 92 (map->NumberOfPages << EFI_PAGE_SHIFT) >= addr + size)) { 93 return (addr); 94 } 95 96 /* Do we fit into new entry? */ 97 if ((map->PhysicalStart > addr) && 98 (map->NumberOfPages >= EFI_SIZE_TO_PAGES(size))) { 99 return (map->PhysicalStart); 100 } 101 } 102 return (0); 103 } 104 105 /* 106 * Find usable address for loading. The address for the kernel is fixed, as 107 * it is determined by kernel linker map (dboot PT_LOAD address). 108 * For modules, we need to consult memory map, the module address has to be 109 * aligned to page boundary and we have to fit into map entry. 110 */ 111 vm_offset_t 112 efi_physaddr(multiboot_tag_module_t *module, vm_offset_t addr, 113 EFI_MEMORY_DESCRIPTOR *map, size_t count, UINTN dsize, vm_offset_t laddr, 114 size_t size) 115 { 116 multiboot_tag_module_t *mp; 117 vm_offset_t off; 118 119 if (addr == 0) 120 return (addr); 121 122 mp = module; 123 do { 124 off = addr; 125 /* Test proposed address */ 126 off = memmap_find(map, count, dsize, off, size); 127 if (off != 0) 128 off = addr_verify(module, off, size); 129 if (off != 0) 130 break; 131 132 /* The module list is exhausted */ 133 if (mp->mb_type != MULTIBOOT_TAG_TYPE_MODULE) 134 break; 135 136 if (mp->mb_mod_start != 0) { 137 addr = roundup2(mp->mb_mod_end + 1, 138 MULTIBOOT_MOD_ALIGN); 139 } 140 mp = (multiboot_tag_module_t *) 141 roundup((uintptr_t)mp + mp->mb_size, MULTIBOOT_TAG_ALIGN); 142 } while (off == 0); 143 144 /* 145 * memmap_find failed to get us address, try to use load 146 * address. 147 */ 148 if (off == 0 || off >= UINT32_MAX) 149 off = addr_verify(module, laddr, size); 150 151 return (off); 152 } 153 154 /* 155 * Allocate pages for data to be loaded. As we can not expect AllocateAddress 156 * to succeed, we allocate using AllocateMaxAddress from 4GB limit. 157 * 4GB limit is because reportedly some 64bit systems are reported to have 158 * issues with memory above 4GB. It should be quite enough anyhow. 159 * Note: AllocateMaxAddress will only make sure we are below the specified 160 * address, we can not make any assumptions about actual location or 161 * about the order of the allocated blocks. 162 */ 163 vm_offset_t 164 efi_loadaddr(uint_t type, void *data, vm_offset_t addr) 165 { 166 EFI_PHYSICAL_ADDRESS paddr; 167 struct stat st; 168 size_t size; 169 uint64_t pages; 170 EFI_STATUS status; 171 172 if (addr == 0) 173 return (addr); /* nothing to do */ 174 175 if (type == LOAD_ELF) 176 return (0); /* not supported */ 177 178 if (type == LOAD_MEM) 179 size = *(size_t *)data; 180 else { 181 stat(data, &st); 182 size = st.st_size; 183 } 184 185 /* AllocatePages can not allocate 0 pages. */ 186 if (size == 0) 187 return (addr); 188 189 pages = EFI_SIZE_TO_PAGES(size); 190 /* 4GB upper limit */ 191 paddr = UINT32_MAX; 192 193 status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 194 pages, &paddr); 195 196 if (EFI_ERROR(status)) { 197 printf("failed to allocate %zu bytes for staging area: %lu\n", 198 size, DECODE_ERROR(status)); 199 return (0); 200 } 201 202 return (paddr); 203 } 204 205 void 206 efi_free_loadaddr(vm_offset_t addr, size_t pages) 207 { 208 (void) BS->FreePages(addr, pages); 209 } 210 211 void * 212 efi_translate(vm_offset_t ptr) 213 { 214 return ((void *)ptr); 215 } 216 217 ssize_t 218 efi_copyin(const void *src, vm_offset_t dest, const size_t len) 219 { 220 if (dest + len >= dest && (uint64_t)dest + len <= UINT32_MAX) { 221 bcopy(src, (void *)(uintptr_t)dest, len); 222 return (len); 223 } else { 224 errno = EFBIG; 225 return (-1); 226 } 227 } 228 229 ssize_t 230 efi_copyout(const vm_offset_t src, void *dest, const size_t len) 231 { 232 if (src + len >= src && (uint64_t)src + len <= UINT32_MAX) { 233 bcopy((void *)(uintptr_t)src, dest, len); 234 return (len); 235 } else { 236 errno = EFBIG; 237 return (-1); 238 } 239 } 240 241 242 ssize_t 243 efi_readin(const int fd, vm_offset_t dest, const size_t len) 244 { 245 if (dest + len >= dest && (uint64_t)dest + len <= UINT32_MAX) { 246 return (read(fd, (void *)dest, len)); 247 } else { 248 errno = EFBIG; 249 return (-1); 250 } 251 } 252 253 /* 254 * Relocate chunks and return pointer to MBI. 255 * This function is relocated before being called and we only have 256 * memmove() available, as most likely moving chunks into the final 257 * destination will destroy the rest of the loader code. 258 * 259 * In safe area we have relocator data, multiboot_tramp, efi_copy_finish, 260 * memmove and stack. 261 */ 262 multiboot2_info_header_t * 263 efi_copy_finish(struct relocator *relocator) 264 { 265 multiboot2_info_header_t *mbi; 266 struct chunk *chunk, *c; 267 struct chunk_head *head; 268 bool done = false; 269 void (*move)(void *s1, const void *s2, size_t n); 270 271 move = (void *)relocator->rel_memmove; 272 273 /* MBI is the last chunk in the list. */ 274 head = &relocator->rel_chunk_head; 275 chunk = STAILQ_LAST(head, chunk, chunk_next); 276 mbi = (multiboot2_info_header_t *)(uintptr_t)chunk->chunk_paddr; 277 278 /* 279 * If chunk paddr == vaddr, the chunk is in place. 280 * If all chunks are in place, we are done. 281 */ 282 chunk = NULL; 283 while (!done) { 284 /* Advance to next item in list. */ 285 if (chunk != NULL) 286 chunk = STAILQ_NEXT(chunk, chunk_next); 287 288 /* 289 * First check if we have anything to do. 290 * We set chunk to NULL every time we move the data. 291 */ 292 done = true; 293 STAILQ_FOREACH_FROM(chunk, head, chunk_next) { 294 if (chunk->chunk_paddr != chunk->chunk_vaddr) { 295 done = false; 296 break; 297 } 298 } 299 if (done) 300 break; 301 302 /* 303 * Make sure the destination is not conflicting 304 * with rest of the modules. 305 */ 306 STAILQ_FOREACH(c, head, chunk_next) { 307 /* Moved already? */ 308 if (c->chunk_vaddr == c->chunk_paddr) 309 continue; 310 311 /* Is it the chunk itself? */ 312 if (c->chunk_vaddr == chunk->chunk_vaddr && 313 c->chunk_size == chunk->chunk_size) 314 continue; 315 316 /* 317 * Check for overlaps. 318 */ 319 if ((c->chunk_vaddr >= chunk->chunk_paddr && 320 c->chunk_vaddr <= 321 chunk->chunk_paddr + chunk->chunk_size) || 322 (c->chunk_vaddr + c->chunk_size >= 323 chunk->chunk_paddr && 324 c->chunk_vaddr + c->chunk_size <= 325 chunk->chunk_paddr + chunk->chunk_size)) { 326 break; 327 } 328 } 329 /* If there are no conflicts, move to place and restart. */ 330 if (c == NULL) { 331 move((void *)(uintptr_t)chunk->chunk_paddr, 332 (void *)(uintptr_t)chunk->chunk_vaddr, 333 chunk->chunk_size); 334 chunk->chunk_vaddr = chunk->chunk_paddr; 335 chunk = NULL; 336 continue; 337 } 338 } 339 340 return (mbi); 341 } 342