xref: /titanic_51/usr/src/boot/sys/boot/efi/loader/copy.c (revision c5e7425f0365f3ad327b8cd9446d510ee3472c9f)
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