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