blob: 93923500b2b48b55bbc7de65272219a28bf977d3 [file] [log] [blame]
Toomas Soomeaf8443c2018-02-03 14:16:26 +02001/*
Toomas Soome199767f2015-10-25 00:06:51 +03002 * 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>
Toomas Soome199767f2015-10-25 00:06:51 +030030
31#include <sys/param.h>
Toomas Soomef9feecc2016-10-09 17:30:28 +030032#include <sys/multiboot2.h>
Toomas Soome199767f2015-10-25 00:06:51 +030033
34#include <stand.h>
35#include <bootstrap.h>
36
37#include <efi.h>
38#include <efilib.h>
Toomas Soomef9feecc2016-10-09 17:30:28 +030039#include <assert.h>
Toomas Soome199767f2015-10-25 00:06:51 +030040
41#include "loader_efi.h"
42
Toomas Soomef9feecc2016-10-09 17:30:28 +030043/*
Toomas Soomeaf8443c2018-02-03 14:16:26 +020044 * Verify the address is not in use by existing modules.
45 */
46static vm_offset_t
47addr_verify(multiboot_tag_module_t *module, vm_offset_t addr, size_t size)
48{
49 vm_offset_t start, end;
50
51 for (;module->mb_type == MULTIBOOT_TAG_TYPE_MODULE;
52 module = (multiboot_tag_module_t *)
53 roundup((uintptr_t)module + module->mb_size, MULTIBOOT_TAG_ALIGN)) {
54
55 start = module->mb_mod_start;
56 end = module->mb_mod_end;
57
58 /* Does this module have address assigned? */
59 if (start == 0)
60 continue;
61
62 if ((start <= addr) && (end >= addr)) {
63 return (0);
64 }
65 if ((start >= addr) && (start <= addr + size)) {
66 return (0);
67 }
68 }
69 return (addr);
70}
71
72/*
73 * Find memory map entry above 1MB, able to contain size bytes from addr.
74 */
75static vm_offset_t
76memmap_find(EFI_MEMORY_DESCRIPTOR *map, size_t count, UINTN dsize,
77 vm_offset_t addr, size_t size)
78{
79 int i;
80
81 for (i = 0; i < count; i++, map = NextMemoryDescriptor(map, dsize)) {
82
83 if (map->Type != EfiConventionalMemory)
84 continue;
85
86 /* We do not want address below 1MB. */
87 if (map->PhysicalStart < 0x100000)
88 continue;
89
90 /* Do we fit into current entry? */
91 if ((map->PhysicalStart <= addr) &&
92 (map->PhysicalStart +
93 (map->NumberOfPages << EFI_PAGE_SHIFT) >= addr + size)) {
94 return (addr);
95 }
96
97 /* Do we fit into new entry? */
98 if ((map->PhysicalStart > addr) &&
99 (map->NumberOfPages >= EFI_SIZE_TO_PAGES(size))) {
100 return (map->PhysicalStart);
101 }
102 }
103 return (0);
104}
105
106/*
107 * Find usable address for loading. The address for the kernel is fixed, as
108 * it is determined by kernel linker map (dboot PT_LOAD address).
109 * For modules, we need to consult memory map, the module address has to be
110 * aligned to page boundary and we have to fit into map entry.
111 */
112vm_offset_t
113efi_physaddr(multiboot_tag_module_t *module, vm_offset_t addr,
114 EFI_MEMORY_DESCRIPTOR *map, size_t count, UINTN dsize, 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 return (off);
145}
146
147/*
Toomas Soomef9feecc2016-10-09 17:30:28 +0300148 * Allocate pages for data to be loaded. As we can not expect AllocateAddress
149 * to succeed, we allocate using AllocateMaxAddress from 4GB limit.
150 * 4GB limit is because reportedly some 64bit systems are reported to have
151 * issues with memory above 4GB. It should be quite enough anyhow.
152 * Note: AllocateMaxAddress will only make sure we are below the specified
153 * address, we can not make any assumptions about actual location or
154 * about the order of the allocated blocks.
155 */
Toomas Soome83b46712016-08-16 19:02:42 +0300156vm_offset_t
157efi_loadaddr(u_int type, void *data, vm_offset_t addr)
Toomas Soome199767f2015-10-25 00:06:51 +0300158{
Toomas Soomef9feecc2016-10-09 17:30:28 +0300159 EFI_PHYSICAL_ADDRESS paddr;
160 struct stat st;
Toomas Soome8600fd42017-09-17 19:55:44 +0300161 size_t size;
Toomas Soomef9feecc2016-10-09 17:30:28 +0300162 uint64_t pages;
163 EFI_STATUS status;
Toomas Soome199767f2015-10-25 00:06:51 +0300164
Toomas Soomef9feecc2016-10-09 17:30:28 +0300165 if (addr == 0)
166 return (addr); /* nothing to do */
167
168 if (type == LOAD_ELF)
169 return (0); /* not supported */
170
171 if (type == LOAD_MEM)
Toomas Soome8600fd42017-09-17 19:55:44 +0300172 size = *(size_t *)data;
Toomas Soomef9feecc2016-10-09 17:30:28 +0300173 else {
174 stat(data, &st);
175 size = st.st_size;
Toomas Soome199767f2015-10-25 00:06:51 +0300176 }
Toomas Soome199767f2015-10-25 00:06:51 +0300177
Toomas Soomef9feecc2016-10-09 17:30:28 +0300178 pages = EFI_SIZE_TO_PAGES(size);
179 /* 4GB upper limit */
180 paddr = 0x0000000100000000;
Toomas Soome199767f2015-10-25 00:06:51 +0300181
Toomas Soomef9feecc2016-10-09 17:30:28 +0300182 status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
183 pages, &paddr);
184
185 if (EFI_ERROR(status)) {
Toomas Soome8600fd42017-09-17 19:55:44 +0300186 printf("failed to allocate %zu bytes for staging area: %lu\n",
Toomas Soomef9feecc2016-10-09 17:30:28 +0300187 size, EFI_ERROR_CODE(status));
188 return (0);
189 }
190
191 return (paddr);
192}
193
194void
Toomas Soome83b46712016-08-16 19:02:42 +0300195efi_free_loadaddr(vm_offset_t addr, size_t pages)
Toomas Soomef9feecc2016-10-09 17:30:28 +0300196{
197 (void) BS->FreePages(addr, pages);
Toomas Soome199767f2015-10-25 00:06:51 +0300198}
199
200void *
201efi_translate(vm_offset_t ptr)
202{
Toomas Soomef9feecc2016-10-09 17:30:28 +0300203 return ((void *)ptr);
Toomas Soome199767f2015-10-25 00:06:51 +0300204}
205
206ssize_t
207efi_copyin(const void *src, vm_offset_t dest, const size_t len)
208{
Toomas Soomef9feecc2016-10-09 17:30:28 +0300209 assert(dest < 0x100000000);
210 bcopy(src, (void *)(uintptr_t)dest, len);
Toomas Soome199767f2015-10-25 00:06:51 +0300211 return (len);
212}
213
214ssize_t
215efi_copyout(const vm_offset_t src, void *dest, const size_t len)
216{
Toomas Soomef9feecc2016-10-09 17:30:28 +0300217 assert(src < 0x100000000);
218 bcopy((void *)(uintptr_t)src, dest, len);
Toomas Soome199767f2015-10-25 00:06:51 +0300219 return (len);
220}
221
222
223ssize_t
224efi_readin(const int fd, vm_offset_t dest, const size_t len)
225{
Toomas Soomef9feecc2016-10-09 17:30:28 +0300226 return (read(fd, (void *)dest, len));
Toomas Soome199767f2015-10-25 00:06:51 +0300227}
228
Toomas Soomef9feecc2016-10-09 17:30:28 +0300229/*
230 * Relocate chunks and return pointer to MBI.
231 * This function is relocated before being called and we only have
232 * memmove() available, as most likely moving chunks into the final
233 * destination will destroy the rest of the loader code.
234 *
235 * In safe area we have relocator data, multiboot_tramp, efi_copy_finish,
236 * memmove and stack.
237 */
238multiboot2_info_header_t *
239efi_copy_finish(struct relocator *relocator)
Toomas Soome199767f2015-10-25 00:06:51 +0300240{
Toomas Soomef9feecc2016-10-09 17:30:28 +0300241 multiboot2_info_header_t *mbi;
242 struct chunk *chunk, *c;
243 struct chunk_head *head;
Toomas Soomeb7b8fda2018-01-18 19:09:30 +0200244 bool done = false;
Toomas Soomef9feecc2016-10-09 17:30:28 +0300245 void (*move)(void *s1, const void *s2, size_t n);
Toomas Soome199767f2015-10-25 00:06:51 +0300246
Toomas Soomef9feecc2016-10-09 17:30:28 +0300247 move = (void *)relocator->rel_memmove;
Toomas Soome199767f2015-10-25 00:06:51 +0300248
Toomas Soomef9feecc2016-10-09 17:30:28 +0300249 /* MBI is the last chunk in the list. */
250 head = &relocator->rel_chunk_head;
251 chunk = STAILQ_LAST(head, chunk, chunk_next);
Toomas Soome83b46712016-08-16 19:02:42 +0300252 mbi = (multiboot2_info_header_t *)(uintptr_t)chunk->chunk_paddr;
Toomas Soomef9feecc2016-10-09 17:30:28 +0300253
254 /*
255 * If chunk paddr == vaddr, the chunk is in place.
256 * If all chunks are in place, we are done.
257 */
258 chunk = NULL;
Toomas Soomeb7b8fda2018-01-18 19:09:30 +0200259 while (!done) {
260 /* Advance to next item in list. */
261 if (chunk != NULL)
262 chunk = STAILQ_NEXT(chunk, chunk_next);
263
264 /*
265 * First check if we have anything to do.
266 * We set chunk to NULL every time we move the data.
267 */
268 done = true;
269 STAILQ_FOREACH_FROM(chunk, head, chunk_next) {
270 if (chunk->chunk_paddr != chunk->chunk_vaddr) {
271 done = false;
272 break;
Toomas Soomef9feecc2016-10-09 17:30:28 +0300273 }
274 }
Toomas Soomeb7b8fda2018-01-18 19:09:30 +0200275 if (done)
Toomas Soomef9feecc2016-10-09 17:30:28 +0300276 break;
277
278 /*
279 * Make sure the destination is not conflicting
280 * with rest of the modules.
281 */
282 STAILQ_FOREACH(c, head, chunk_next) {
283 /* Moved already? */
284 if (c->chunk_vaddr == c->chunk_paddr)
285 continue;
Toomas Soomeb7b8fda2018-01-18 19:09:30 +0200286
Toomas Soomef9feecc2016-10-09 17:30:28 +0300287 /* Is it the chunk itself? */
288 if (c->chunk_vaddr == chunk->chunk_vaddr &&
289 c->chunk_size == chunk->chunk_size)
290 continue;
Toomas Soomeb7b8fda2018-01-18 19:09:30 +0200291
292 /*
293 * Check for overlaps.
294 */
Toomas Soomef9feecc2016-10-09 17:30:28 +0300295 if ((c->chunk_vaddr >= chunk->chunk_paddr &&
296 c->chunk_vaddr <=
297 chunk->chunk_paddr + chunk->chunk_size) ||
298 (c->chunk_vaddr + c->chunk_size >=
299 chunk->chunk_paddr &&
300 c->chunk_vaddr + c->chunk_size <=
Toomas Soomeb7b8fda2018-01-18 19:09:30 +0200301 chunk->chunk_paddr + chunk->chunk_size)) {
Toomas Soomef9feecc2016-10-09 17:30:28 +0300302 break;
Toomas Soomeb7b8fda2018-01-18 19:09:30 +0200303 }
Toomas Soomef9feecc2016-10-09 17:30:28 +0300304 }
305 /* If there are no conflicts, move to place and restart. */
306 if (c == NULL) {
Toomas Soome83b46712016-08-16 19:02:42 +0300307 move((void *)(uintptr_t)chunk->chunk_paddr,
308 (void *)(uintptr_t)chunk->chunk_vaddr,
Toomas Soomef9feecc2016-10-09 17:30:28 +0300309 chunk->chunk_size);
310 chunk->chunk_vaddr = chunk->chunk_paddr;
311 chunk = NULL;
312 continue;
313 }
Toomas Soomef9feecc2016-10-09 17:30:28 +0300314 }
315
316 return (mbi);
Toomas Soome199767f2015-10-25 00:06:51 +0300317}