exec.c revision 1.17 1 /* $NetBSD: exec.c,v 1.17 2020/06/26 03:23:04 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2019 Jason R. Thorpe
5 * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include "efiboot.h"
31 #include "efienv.h"
32 #include "efifdt.h"
33 #include "efiacpi.h"
34 #include "efirng.h"
35 #include "module.h"
36 #include "overlay.h"
37
38 #include <sys/param.h>
39 #include <sys/reboot.h>
40
41 extern char twiddle_toggle;
42
43 u_long load_offset = 0;
44
45 #define FDT_SPACE (4 * 1024 * 1024)
46 #define FDT_ALIGN ((2 * 1024 * 1024) - 1)
47
48 static EFI_PHYSICAL_ADDRESS initrd_addr, dtb_addr, rndseed_addr, efirng_addr;
49 static u_long initrd_size = 0, dtb_size = 0, rndseed_size = 0, efirng_size = 0;
50
51 static int
52 load_file(const char *path, u_long extra, bool quiet_errors,
53 EFI_PHYSICAL_ADDRESS *paddr, u_long *psize)
54 {
55 EFI_STATUS status;
56 struct stat st;
57 ssize_t len;
58 ssize_t expectedlen;
59 int fd;
60
61 if (strlen(path) == 0)
62 return 0;
63
64 fd = open(path, 0);
65 if (fd < 0) {
66 if (!quiet_errors) {
67 printf("boot: failed to open %s: %s\n", path,
68 strerror(errno));
69 }
70 return errno;
71 }
72 if (fstat(fd, &st) < 0) {
73 printf("boot: failed to fstat %s: %s\n", path, strerror(errno));
74 close(fd);
75 return errno;
76 }
77 if (st.st_size == 0) {
78 if (!quiet_errors) {
79 printf("boot: empty file %s\n", path);
80 }
81 close(fd);
82 return EINVAL;
83 }
84
85 expectedlen = st.st_size;
86 *psize = st.st_size + extra;
87
88 #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
89 *paddr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
90 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
91 EFI_SIZE_TO_PAGES(*psize), paddr);
92 #else
93 *paddr = 0;
94 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
95 EFI_SIZE_TO_PAGES(*psize), paddr);
96 #endif
97 if (EFI_ERROR(status)) {
98 printf("Failed to allocate %lu bytes for %s (error %lu)\n",
99 *psize, path, (u_long)status);
100 close(fd);
101 *paddr = 0;
102 return ENOMEM;
103 }
104
105 printf("boot: loading %s ", path);
106 len = read(fd, (void *)(uintptr_t)*paddr, expectedlen);
107 close(fd);
108
109 if (len != expectedlen) {
110 if (len < 0) {
111 printf(": %s\n", strerror(errno));
112 } else {
113 printf(": returned %ld (expected %ld)\n", len,
114 expectedlen);
115 }
116 return EIO;
117 }
118
119 printf("done.\n");
120
121 efi_dcache_flush(*paddr, *psize);
122
123 return 0;
124 }
125
126 static void
127 apply_overlay(const char *path, void *dtbo)
128 {
129
130 if (!efi_fdt_overlay_is_compatible(dtbo)) {
131 printf("boot: %s: incompatible overlay\n", path);
132 return;
133 }
134
135 int fdterr;
136
137 if (efi_fdt_overlay_apply(dtbo, &fdterr) != 0) {
138 printf("boot: %s: error %d applying overlay\n", path, fdterr);
139 }
140 }
141
142 static void
143 apply_overlay_file(const char *path)
144 {
145 EFI_PHYSICAL_ADDRESS dtbo_addr;
146 u_long dtbo_size;
147
148 if (strlen(path) == 0)
149 return;
150
151 if (load_file(path, 0, false, &dtbo_addr, &dtbo_size) != 0 ||
152 dtbo_addr == 0) {
153 /* Error messages have already been displayed. */
154 goto out;
155 }
156
157 apply_overlay(path, (void *)(uintptr_t)dtbo_addr);
158
159 out:
160 if (dtbo_addr) {
161 uefi_call_wrapper(BS->FreePages, 2, dtbo_addr,
162 EFI_SIZE_TO_PAGES(dtbo_size));
163 }
164 }
165
166 static void
167 load_fdt_overlays(void)
168 {
169 if (!dtoverlay_enabled)
170 return;
171
172 dtoverlay_foreach(apply_overlay_file);
173 }
174
175 static void
176 load_module(const char *module_name)
177 {
178 EFI_PHYSICAL_ADDRESS addr;
179 u_long size;
180 char path[PATH_MAX];
181
182 snprintf(path, sizeof(path), "%s/%s/%s.kmod", module_prefix,
183 module_name, module_name);
184
185 if (load_file(path, 0, false, &addr, &size) != 0 || addr == 0 || size == 0)
186 return;
187
188 efi_fdt_module(module_name, (u_long)addr, size);
189 }
190
191 static void
192 load_modules(const char *kernel_name)
193 {
194 if (!module_enabled)
195 return;
196
197 module_init(kernel_name);
198 module_foreach(load_module);
199 }
200
201 static void
202 generate_efirng(void)
203 {
204 EFI_PHYSICAL_ADDRESS addr;
205 u_long size = EFI_PAGE_SIZE;
206 EFI_STATUS status;
207
208 /* Check whether the RNG is available before bothering. */
209 if (!efi_rng_available())
210 return;
211
212 /*
213 * Allocate a page. This is the smallest unit we can pass into
214 * the kernel conveniently.
215 */
216 #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
217 addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
218 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress,
219 EfiLoaderData, EFI_SIZE_TO_PAGES(size), &addr);
220 #else
221 addr = 0;
222 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages,
223 EfiLoaderData, EFI_SIZE_TO_PAGES(size), &addr);
224 #endif
225 if (EFI_ERROR(status)) {
226 Print(L"Failed to allocate page for EFI RNG output: %r\n",
227 status);
228 return;
229 }
230
231 /* Fill the page with whatever the EFI RNG will do. */
232 if (efi_rng((void *)(uintptr_t)addr, size)) {
233 uefi_call_wrapper(BS->FreePages, 2, addr, size);
234 return;
235 }
236
237 /* Success! */
238 efirng_addr = addr;
239 efirng_size = size;
240 }
241
242 int
243 exec_netbsd(const char *fname, const char *args)
244 {
245 EFI_PHYSICAL_ADDRESS addr;
246 u_long marks[MARK_MAX], alloc_size;
247 EFI_STATUS status;
248 int fd, ohowto;
249
250 load_file(get_initrd_path(), 0, false, &initrd_addr, &initrd_size);
251 load_file(get_dtb_path(), 0, false, &dtb_addr, &dtb_size);
252 generate_efirng();
253
254 memset(marks, 0, sizeof(marks));
255 ohowto = howto;
256 howto |= AB_SILENT;
257 fd = loadfile(fname, marks, COUNT_KERNEL | LOAD_NOTE);
258 howto = ohowto;
259 if (fd < 0) {
260 printf("boot: %s: %s\n", fname, strerror(errno));
261 return EIO;
262 }
263 close(fd);
264 marks[MARK_END] = (((u_long) marks[MARK_END] + sizeof(int) - 1)) & (-sizeof(int));
265 alloc_size = marks[MARK_END] - marks[MARK_START] + FDT_SPACE + EFIBOOT_ALIGN;
266
267 #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
268 addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
269 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
270 EFI_SIZE_TO_PAGES(alloc_size), &addr);
271 #else
272 addr = 0;
273 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
274 EFI_SIZE_TO_PAGES(alloc_size), &addr);
275 #endif
276 if (EFI_ERROR(status)) {
277 printf("Failed to allocate %lu bytes for kernel image (error %lu)\n",
278 alloc_size, (u_long)status);
279 return ENOMEM;
280 }
281
282 memset(marks, 0, sizeof(marks));
283 load_offset = (addr + EFIBOOT_ALIGN) & ~(EFIBOOT_ALIGN - 1);
284 fd = loadfile(fname, marks, LOAD_KERNEL);
285 if (fd < 0) {
286 printf("boot: %s: %s\n", fname, strerror(errno));
287 goto cleanup;
288 }
289 close(fd);
290 load_offset = 0;
291
292 #ifdef EFIBOOT_ACPI
293 if (efi_acpi_available()) {
294 efi_acpi_create_fdt();
295 } else
296 #endif
297 if (dtb_addr && efi_fdt_set_data((void *)(uintptr_t)dtb_addr) != 0) {
298 printf("boot: invalid DTB data\n");
299 goto cleanup;
300 }
301
302 if (efi_fdt_size() > 0) {
303 /*
304 * Load the rndseed as late as possible -- after we
305 * have committed to using fdt and executing this
306 * kernel -- so that it doesn't hang around in memory
307 * if we have to bail or the kernel won't use it.
308 */
309 load_file(get_rndseed_path(), 0, false,
310 &rndseed_addr, &rndseed_size);
311
312 efi_fdt_init((marks[MARK_END] + FDT_ALIGN) & ~FDT_ALIGN, FDT_ALIGN + 1);
313 load_modules(fname);
314 load_fdt_overlays();
315 efi_fdt_initrd(initrd_addr, initrd_size);
316 efi_fdt_rndseed(rndseed_addr, rndseed_size);
317 efi_fdt_efirng(efirng_addr, efirng_size);
318 efi_fdt_bootargs(args);
319 #ifdef EFIBOOT_ACPI
320 if (efi_acpi_available())
321 efi_fdt_gop();
322 #endif
323 efi_fdt_memory_map();
324 }
325
326 efi_cleanup();
327
328 if (efi_fdt_size() > 0) {
329 efi_fdt_fini();
330 }
331
332 efi_boot_kernel(marks);
333
334 /* This should not happen.. */
335 printf("boot returned\n");
336
337 cleanup:
338 uefi_call_wrapper(BS->FreePages, 2, addr, EFI_SIZE_TO_PAGES(alloc_size));
339 if (initrd_addr) {
340 uefi_call_wrapper(BS->FreePages, 2, initrd_addr, EFI_SIZE_TO_PAGES(initrd_size));
341 initrd_addr = 0;
342 initrd_size = 0;
343 }
344 if (dtb_addr) {
345 uefi_call_wrapper(BS->FreePages, 2, dtb_addr, EFI_SIZE_TO_PAGES(dtb_size));
346 dtb_addr = 0;
347 dtb_size = 0;
348 }
349 return EIO;
350 }
351