xref: /illumos-gate/usr/src/boot/sys/boot/efi/loader/copy.c (revision 873f5d07)
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, size_t size)
114 {
115 	multiboot_tag_module_t *mp;
116 	vm_offset_t off;
117 
118 	if (addr == 0)
119 		return (addr);
120 
121 	mp = module;
122 	do {
123 		off = addr;
124 		/* Test proposed address */
125 		off = memmap_find(map, count, dsize, off, size);
126 		if (off != 0)
127 			off = addr_verify(module, off, size);
128 		if (off != 0)
129 			break;
130 
131 		/* The module list is exhausted */
132 		if (mp->mb_type != MULTIBOOT_TAG_TYPE_MODULE)
133 			break;
134 
135 		if (mp->mb_mod_start != 0) {
136 			addr = roundup2(mp->mb_mod_end + 1,
137 			    MULTIBOOT_MOD_ALIGN);
138 		}
139 		mp = (multiboot_tag_module_t *)
140 		    roundup((uintptr_t)mp + mp->mb_size, MULTIBOOT_TAG_ALIGN);
141 	} while (off == 0);
142 
143 	return (off);
144 }
145 
146 /*
147  * Allocate pages for data to be loaded. As we can not expect AllocateAddress
148  * to succeed, we allocate using AllocateMaxAddress from 4GB limit.
149  * 4GB limit is because reportedly some 64bit systems are reported to have
150  * issues with memory above 4GB. It should be quite enough anyhow.
151  * Note: AllocateMaxAddress will only make sure we are below the specified
152  * address, we can not make any assumptions about actual location or
153  * about the order of the allocated blocks.
154  */
155 vm_offset_t
156 efi_loadaddr(uint_t type, void *data, vm_offset_t addr)
157 {
158 	EFI_PHYSICAL_ADDRESS paddr;
159 	struct stat st;
160 	size_t size;
161 	uint64_t pages;
162 	EFI_STATUS status;
163 
164 	if (addr == 0)
165 		return (addr);	/* nothing to do */
166 
167 	if (type == LOAD_ELF)
168 		return (0);	/* not supported */
169 
170 	if (type == LOAD_MEM)
171 		size = *(size_t *)data;
172 	else {
173 		stat(data, &st);
174 		size = st.st_size;
175 	}
176 
177 	/* AllocatePages can not allocate 0 pages. */
178 	if (size == 0)
179 		return (addr);
180 
181 	pages = EFI_SIZE_TO_PAGES(size);
182 	/* 4GB upper limit */
183 	paddr = UINT32_MAX;
184 
185 	status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData,
186 	    pages, &paddr);
187 
188 	if (EFI_ERROR(status)) {
189 		printf("failed to allocate %zu bytes for staging area: %lu\n",
190 		    size, EFI_ERROR_CODE(status));
191 		return (0);
192 	}
193 
194 	return (paddr);
195 }
196 
197 void
198 efi_free_loadaddr(vm_offset_t addr, size_t pages)
199 {
200 	(void) BS->FreePages(addr, pages);
201 }
202 
203 void *
204 efi_translate(vm_offset_t ptr)
205 {
206 	return ((void *)ptr);
207 }
208 
209 ssize_t
210 efi_copyin(const void *src, vm_offset_t dest, const size_t len)
211 {
212 	if (dest + len >= dest && (uint64_t)dest + len <= UINT32_MAX) {
213 		bcopy(src, (void *)(uintptr_t)dest, len);
214 		return (len);
215 	} else {
216 		errno = EFBIG;
217 		return (-1);
218 	}
219 }
220 
221 ssize_t
222 efi_copyout(const vm_offset_t src, void *dest, const size_t len)
223 {
224 	if (src + len >= src && (uint64_t)src + len <= UINT32_MAX) {
225 		bcopy((void *)(uintptr_t)src, dest, len);
226 		return (len);
227 	} else {
228 		errno = EFBIG;
229 		return (-1);
230 	}
231 }
232 
233 
234 ssize_t
235 efi_readin(const int fd, vm_offset_t dest, const size_t len)
236 {
237 	if (dest + len >= dest && (uint64_t)dest + len <= UINT32_MAX) {
238 		return (read(fd, (void *)dest, len));
239 	} else {
240 		errno = EFBIG;
241 		return (-1);
242 	}
243 }
244 
245 /*
246  * Relocate chunks and return pointer to MBI.
247  * This function is relocated before being called and we only have
248  * memmove() available, as most likely moving chunks into the final
249  * destination will destroy the rest of the loader code.
250  *
251  * In safe area we have relocator data, multiboot_tramp, efi_copy_finish,
252  * memmove and stack.
253  */
254 multiboot2_info_header_t *
255 efi_copy_finish(struct relocator *relocator)
256 {
257 	multiboot2_info_header_t *mbi;
258 	struct chunk *chunk, *c;
259 	struct chunk_head *head;
260 	bool done = false;
261 	void (*move)(void *s1, const void *s2, size_t n);
262 
263 	move = (void *)relocator->rel_memmove;
264 
265 	/* MBI is the last chunk in the list. */
266 	head = &relocator->rel_chunk_head;
267 	chunk = STAILQ_LAST(head, chunk, chunk_next);
268 	mbi = (multiboot2_info_header_t *)(uintptr_t)chunk->chunk_paddr;
269 
270 	/*
271 	 * If chunk paddr == vaddr, the chunk is in place.
272 	 * If all chunks are in place, we are done.
273 	 */
274 	chunk = NULL;
275 	while (!done) {
276 		/* Advance to next item in list. */
277 		if (chunk != NULL)
278 			chunk = STAILQ_NEXT(chunk, chunk_next);
279 
280 		/*
281 		 * First check if we have anything to do.
282 		 * We set chunk to NULL every time we move the data.
283 		 */
284 		done = true;
285 		STAILQ_FOREACH_FROM(chunk, head, chunk_next) {
286 			if (chunk->chunk_paddr != chunk->chunk_vaddr) {
287 				done = false;
288 				break;
289 			}
290 		}
291 		if (done)
292 			break;
293 
294 		/*
295 		 * Make sure the destination is not conflicting
296 		 * with rest of the modules.
297 		 */
298 		STAILQ_FOREACH(c, head, chunk_next) {
299 			/* Moved already? */
300 			if (c->chunk_vaddr == c->chunk_paddr)
301 				continue;
302 
303 			/* Is it the chunk itself? */
304 			if (c->chunk_vaddr == chunk->chunk_vaddr &&
305 			    c->chunk_size == chunk->chunk_size)
306 				continue;
307 
308 			/*
309 			 * Check for overlaps.
310 			 */
311 			if ((c->chunk_vaddr >= chunk->chunk_paddr &&
312 			    c->chunk_vaddr <=
313 			    chunk->chunk_paddr + chunk->chunk_size) ||
314 			    (c->chunk_vaddr + c->chunk_size >=
315 			    chunk->chunk_paddr &&
316 			    c->chunk_vaddr + c->chunk_size <=
317 			    chunk->chunk_paddr + chunk->chunk_size)) {
318 				break;
319 			}
320 		}
321 		/* If there are no conflicts, move to place and restart. */
322 		if (c == NULL) {
323 			move((void *)(uintptr_t)chunk->chunk_paddr,
324 			    (void *)(uintptr_t)chunk->chunk_vaddr,
325 			    chunk->chunk_size);
326 			chunk->chunk_vaddr = chunk->chunk_paddr;
327 			chunk = NULL;
328 			continue;
329 		}
330 	}
331 
332 	return (mbi);
333 }
334