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
efi_loadaddr(u_int type,void * data,uint64_t addr)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
efi_free_loadaddr(uint64_t addr,uint64_t pages)91 efi_free_loadaddr(uint64_t addr, uint64_t pages)
92 {
93 (void) BS->FreePages(addr, pages);
94 }
95
96 void *
efi_translate(vm_offset_t ptr)97 efi_translate(vm_offset_t ptr)
98 {
99 return ((void *)ptr);
100 }
101
102 ssize_t
efi_copyin(const void * src,vm_offset_t dest,const size_t len)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
efi_copyout(const vm_offset_t src,void * dest,const size_t len)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
efi_readin(const int fd,vm_offset_t dest,const size_t len)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 *
efi_copy_finish(struct relocator * relocator)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