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