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 #include <assert.h> 40 41 #include "loader_efi.h" 42 43 /* 44 * Allocate pages for data to be loaded. As we can not expect AllocateAddress 45 * to succeed, we allocate using AllocateMaxAddress from 4GB limit. 46 * 4GB limit is because reportedly some 64bit systems are reported to have 47 * issues with memory above 4GB. It should be quite enough anyhow. 48 * Note: AllocateMaxAddress will only make sure we are below the specified 49 * address, we can not make any assumptions about actual location or 50 * about the order of the allocated blocks. 51 */ 52 uint64_t 53 efi_loadaddr(u_int type, void *data, uint64_t addr) 54 { 55 EFI_PHYSICAL_ADDRESS paddr; 56 struct stat st; 57 size_t size; 58 uint64_t pages; 59 EFI_STATUS status; 60 61 if (addr == 0) 62 return (addr); /* nothing to do */ 63 64 if (type == LOAD_ELF) 65 return (0); /* not supported */ 66 67 if (type == LOAD_MEM) 68 size = *(size_t *)data; 69 else { 70 stat(data, &st); 71 size = st.st_size; 72 } 73 74 pages = EFI_SIZE_TO_PAGES(size); 75 /* 4GB upper limit */ 76 paddr = 0x0000000100000000; 77 78 status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 79 pages, &paddr); 80 81 if (EFI_ERROR(status)) { 82 printf("failed to allocate %zu bytes for staging area: %lu\n", 83 size, EFI_ERROR_CODE(status)); 84 return (0); 85 } 86 87 return (paddr); 88 } 89 90 void 91 efi_free_loadaddr(uint64_t addr, uint64_t pages) 92 { 93 (void) BS->FreePages(addr, pages); 94 } 95 96 void * 97 efi_translate(vm_offset_t ptr) 98 { 99 return ((void *)ptr); 100 } 101 102 ssize_t 103 efi_copyin(const void *src, vm_offset_t dest, const size_t len) 104 { 105 assert(dest < 0x100000000); 106 bcopy(src, (void *)(uintptr_t)dest, len); 107 return (len); 108 } 109 110 ssize_t 111 efi_copyout(const vm_offset_t src, void *dest, const size_t len) 112 { 113 assert(src < 0x100000000); 114 bcopy((void *)(uintptr_t)src, dest, len); 115 return (len); 116 } 117 118 119 ssize_t 120 efi_readin(const int fd, vm_offset_t dest, const size_t len) 121 { 122 return (read(fd, (void *)dest, len)); 123 } 124 125 /* 126 * Relocate chunks and return pointer to MBI. 127 * This function is relocated before being called and we only have 128 * memmove() available, as most likely moving chunks into the final 129 * destination will destroy the rest of the loader code. 130 * 131 * In safe area we have relocator data, multiboot_tramp, efi_copy_finish, 132 * memmove and stack. 133 */ 134 multiboot2_info_header_t * 135 efi_copy_finish(struct relocator *relocator) 136 { 137 multiboot2_info_header_t *mbi; 138 struct chunk *chunk, *c; 139 struct chunk_head *head; 140 UINT64 size; 141 int done = 0; 142 void (*move)(void *s1, const void *s2, size_t n); 143 144 move = (void *)relocator->rel_memmove; 145 146 /* MBI is the last chunk in the list. */ 147 head = &relocator->rel_chunk_head; 148 chunk = STAILQ_LAST(head, chunk, chunk_next); 149 mbi = (multiboot2_info_header_t *)chunk->chunk_paddr; 150 151 /* 152 * If chunk paddr == vaddr, the chunk is in place. 153 * If all chunks are in place, we are done. 154 */ 155 chunk = NULL; 156 while (done == 0) { 157 /* First check if we have anything to do. */ 158 if (chunk == NULL) { 159 done = 1; 160 STAILQ_FOREACH(chunk, head, chunk_next) { 161 if (chunk->chunk_paddr != chunk->chunk_vaddr) { 162 done = 0; 163 break; 164 } 165 } 166 } 167 if (done == 1) 168 break; 169 170 /* 171 * Make sure the destination is not conflicting 172 * with rest of the modules. 173 */ 174 STAILQ_FOREACH(c, head, chunk_next) { 175 /* Moved already? */ 176 if (c->chunk_vaddr == c->chunk_paddr) 177 continue; 178 /* Is it the chunk itself? */ 179 if (c->chunk_vaddr == chunk->chunk_vaddr && 180 c->chunk_size == chunk->chunk_size) 181 continue; 182 if ((c->chunk_vaddr >= chunk->chunk_paddr && 183 c->chunk_vaddr <= 184 chunk->chunk_paddr + chunk->chunk_size) || 185 (c->chunk_vaddr + c->chunk_size >= 186 chunk->chunk_paddr && 187 c->chunk_vaddr + c->chunk_size <= 188 chunk->chunk_paddr + chunk->chunk_size)) 189 break; 190 } 191 /* If there are no conflicts, move to place and restart. */ 192 if (c == NULL) { 193 move((void *)chunk->chunk_paddr, 194 (void *)chunk->chunk_vaddr, 195 chunk->chunk_size); 196 chunk->chunk_vaddr = chunk->chunk_paddr; 197 chunk = NULL; 198 continue; 199 } 200 chunk = STAILQ_NEXT(chunk, chunk_next); 201 } 202 203 return (mbi); 204 } 205