exec.c revision 1.10 1 1.10 thorpej /* $NetBSD: exec.c,v 1.10 2019/04/21 22:30:41 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.1 jmcneill
35 1.6 jmcneill #include <sys/reboot.h>
36 1.1 jmcneill
37 1.3 jmcneill u_long load_offset = 0;
38 1.3 jmcneill
39 1.4 jmcneill #define FDT_SPACE (4 * 1024 * 1024)
40 1.4 jmcneill #define FDT_ALIGN ((2 * 1024 * 1024) - 1)
41 1.4 jmcneill
42 1.5 jmcneill static EFI_PHYSICAL_ADDRESS initrd_addr, dtb_addr;
43 1.5 jmcneill static u_long initrd_size = 0, dtb_size = 0;
44 1.4 jmcneill
45 1.4 jmcneill static int
46 1.10 thorpej load_file(const char *path, u_long extra, bool quiet_errors,
47 1.10 thorpej EFI_PHYSICAL_ADDRESS *paddr, u_long *psize)
48 1.4 jmcneill {
49 1.4 jmcneill EFI_STATUS status;
50 1.4 jmcneill struct stat st;
51 1.4 jmcneill ssize_t len;
52 1.10 thorpej ssize_t expectedlen;
53 1.4 jmcneill int fd;
54 1.4 jmcneill
55 1.4 jmcneill if (strlen(path) == 0)
56 1.4 jmcneill return 0;
57 1.4 jmcneill
58 1.4 jmcneill fd = open(path, 0);
59 1.4 jmcneill if (fd < 0) {
60 1.10 thorpej if (!quiet_errors) {
61 1.10 thorpej printf("boot: failed to open %s: %s\n", path,
62 1.10 thorpej strerror(errno));
63 1.10 thorpej }
64 1.4 jmcneill return errno;
65 1.4 jmcneill }
66 1.4 jmcneill if (fstat(fd, &st) < 0) {
67 1.4 jmcneill printf("boot: failed to fstat %s: %s\n", path, strerror(errno));
68 1.4 jmcneill close(fd);
69 1.4 jmcneill return errno;
70 1.4 jmcneill }
71 1.4 jmcneill if (st.st_size == 0) {
72 1.10 thorpej if (!quiet_errors) {
73 1.10 thorpej printf("boot: empty file %s\n", path);
74 1.10 thorpej }
75 1.4 jmcneill close(fd);
76 1.4 jmcneill return EINVAL;
77 1.4 jmcneill }
78 1.4 jmcneill
79 1.10 thorpej expectedlen = st.st_size;
80 1.10 thorpej *psize = st.st_size + extra;
81 1.4 jmcneill
82 1.4 jmcneill #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
83 1.5 jmcneill *paddr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
84 1.4 jmcneill status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
85 1.5 jmcneill EFI_SIZE_TO_PAGES(*psize), paddr);
86 1.4 jmcneill #else
87 1.5 jmcneill *paddr = 0;
88 1.4 jmcneill status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
89 1.5 jmcneill EFI_SIZE_TO_PAGES(*psize), paddr);
90 1.4 jmcneill #endif
91 1.4 jmcneill if (EFI_ERROR(status)) {
92 1.5 jmcneill printf("Failed to allocate %lu bytes for %s (error %lu)\n",
93 1.9 jmcneill *psize, path, (u_long)status);
94 1.4 jmcneill close(fd);
95 1.10 thorpej *paddr = 0;
96 1.4 jmcneill return ENOMEM;
97 1.4 jmcneill }
98 1.4 jmcneill
99 1.4 jmcneill printf("boot: loading %s ", path);
100 1.10 thorpej len = read(fd, (void *)(uintptr_t)*paddr, expectedlen);
101 1.4 jmcneill close(fd);
102 1.4 jmcneill
103 1.10 thorpej if (len != expectedlen) {
104 1.10 thorpej if (len < 0) {
105 1.4 jmcneill printf(": %s\n", strerror(errno));
106 1.10 thorpej } else {
107 1.10 thorpej printf(": returned %ld (expected %ld)\n", len,
108 1.10 thorpej expectedlen);
109 1.10 thorpej }
110 1.4 jmcneill return EIO;
111 1.4 jmcneill }
112 1.4 jmcneill
113 1.4 jmcneill printf("done.\n");
114 1.4 jmcneill
115 1.5 jmcneill efi_dcache_flush(*paddr, *psize);
116 1.4 jmcneill
117 1.4 jmcneill return 0;
118 1.4 jmcneill }
119 1.4 jmcneill
120 1.10 thorpej static const char default_efibootplist_path[] = "/etc/efiboot.plist";
121 1.10 thorpej
122 1.10 thorpej /* This is here because load_file() is here. */
123 1.10 thorpej void
124 1.10 thorpej load_efibootplist(bool default_fallback)
125 1.10 thorpej {
126 1.10 thorpej EFI_PHYSICAL_ADDRESS plist_addr = 0;
127 1.10 thorpej u_long plist_size = 0;
128 1.10 thorpej prop_dictionary_t plist = NULL, oplist = NULL;
129 1.10 thorpej bool load_quietly = false;
130 1.10 thorpej
131 1.10 thorpej const char *path = get_efibootplist_path();
132 1.10 thorpej if (path == NULL || strlen(path) == 0) {
133 1.10 thorpej if (!default_fallback)
134 1.10 thorpej return;
135 1.10 thorpej path = default_efibootplist_path;
136 1.10 thorpej load_quietly = true;
137 1.10 thorpej }
138 1.10 thorpej
139 1.10 thorpej /*
140 1.10 thorpej * Fudge the size so we can ensure the resulting buffer
141 1.10 thorpej * is NUL-terminated for convenience.
142 1.10 thorpej */
143 1.10 thorpej if (load_file(path, 1, load_quietly, &plist_addr, &plist_size) != 0 ||
144 1.10 thorpej plist_addr == 0) {
145 1.10 thorpej /* Error messages have already been displayed. */
146 1.10 thorpej goto out;
147 1.10 thorpej }
148 1.10 thorpej char *plist_buf = (char *)((uintptr_t)plist_addr);
149 1.10 thorpej plist_buf[plist_size - 1] = '\0';
150 1.10 thorpej
151 1.10 thorpej plist = prop_dictionary_internalize(plist_buf);
152 1.10 thorpej if (plist == NULL) {
153 1.10 thorpej printf("boot: unable to parse plist '%s'\n", path);
154 1.10 thorpej goto out;
155 1.10 thorpej }
156 1.10 thorpej
157 1.10 thorpej out:
158 1.10 thorpej oplist = efibootplist;
159 1.10 thorpej
160 1.10 thorpej /*
161 1.10 thorpej * If we had a failure, create an empty one for
162 1.10 thorpej * convenience. But a failure should not clobber
163 1.10 thorpej * an in-memory plist we already have.
164 1.10 thorpej */
165 1.10 thorpej if (plist == NULL &&
166 1.10 thorpej (oplist == NULL || prop_dictionary_count(oplist) == 0))
167 1.10 thorpej plist = prop_dictionary_create();
168 1.10 thorpej
169 1.10 thorpej #ifdef EFIBOOT_DEBUG
170 1.10 thorpej printf(">> load_efibootplist: oplist = 0x%lx, plist = 0x%lx\n",
171 1.10 thorpej (u_long)oplist, (u_long)plist);
172 1.10 thorpej #endif
173 1.10 thorpej
174 1.10 thorpej if (plist_addr) {
175 1.10 thorpej uefi_call_wrapper(BS->FreePages, 2, plist_addr,
176 1.10 thorpej EFI_SIZE_TO_PAGES(plist_size));
177 1.10 thorpej }
178 1.10 thorpej
179 1.10 thorpej if (plist) {
180 1.10 thorpej efibootplist = plist;
181 1.10 thorpej efi_env_from_efibootplist();
182 1.10 thorpej
183 1.10 thorpej if (oplist)
184 1.10 thorpej prop_object_release(oplist);
185 1.10 thorpej }
186 1.10 thorpej }
187 1.10 thorpej
188 1.10 thorpej static void
189 1.10 thorpej apply_overlay(void *dtbo)
190 1.10 thorpej {
191 1.10 thorpej
192 1.10 thorpej if (!efi_fdt_overlay_is_compatible(dtbo)) {
193 1.10 thorpej printf("boot: incompatible overlay\n");
194 1.10 thorpej }
195 1.10 thorpej
196 1.10 thorpej int fdterr;
197 1.10 thorpej
198 1.10 thorpej if (efi_fdt_overlay_apply(dtbo, &fdterr) != 0) {
199 1.10 thorpej printf("boot: error %d applying overlay\n", fdterr);
200 1.10 thorpej }
201 1.10 thorpej }
202 1.10 thorpej
203 1.10 thorpej static void
204 1.10 thorpej apply_overlay_file(const char *path)
205 1.10 thorpej {
206 1.10 thorpej EFI_PHYSICAL_ADDRESS dtbo_addr;
207 1.10 thorpej u_long dtbo_size;
208 1.10 thorpej
209 1.10 thorpej if (strlen(path) == 0)
210 1.10 thorpej return;
211 1.10 thorpej
212 1.10 thorpej if (load_file(path, 0, false, &dtbo_addr, &dtbo_size) != 0 ||
213 1.10 thorpej dtbo_addr == 0) {
214 1.10 thorpej /* Error messages have already been displayed. */
215 1.10 thorpej goto out;
216 1.10 thorpej }
217 1.10 thorpej
218 1.10 thorpej apply_overlay((void *)(uintptr_t)dtbo_addr);
219 1.10 thorpej
220 1.10 thorpej out:
221 1.10 thorpej if (dtbo_addr) {
222 1.10 thorpej uefi_call_wrapper(BS->FreePages, 2, dtbo_addr,
223 1.10 thorpej EFI_SIZE_TO_PAGES(dtbo_size));
224 1.10 thorpej }
225 1.10 thorpej }
226 1.10 thorpej
227 1.10 thorpej #define DT_OVERLAYS_PROP "device-tree-overlays"
228 1.10 thorpej
229 1.10 thorpej static void
230 1.10 thorpej load_fdt_overlays(void)
231 1.10 thorpej {
232 1.10 thorpej /*
233 1.10 thorpej * We support loading device tree overlays specified in efiboot.plist
234 1.10 thorpej * using the following schema:
235 1.10 thorpej *
236 1.10 thorpej * <key>device-tree-overlays</key>
237 1.10 thorpej * <array>
238 1.10 thorpej * <string>/path/to/some/overlay.dtbo</string>
239 1.10 thorpej * <string>hd0e:/some/other/overlay.dtbo</string>
240 1.10 thorpej * </array>
241 1.10 thorpej *
242 1.10 thorpej * The overlays are loaded in array order.
243 1.10 thorpej */
244 1.10 thorpej prop_array_t overlays = prop_dictionary_get(efibootplist,
245 1.10 thorpej DT_OVERLAYS_PROP);
246 1.10 thorpej if (overlays == NULL) {
247 1.10 thorpej #ifdef EFIBOOT_DEBUG
248 1.10 thorpej printf("boot: no device-tree-overlays\n");
249 1.10 thorpej #endif
250 1.10 thorpej return;
251 1.10 thorpej }
252 1.10 thorpej if (prop_object_type(overlays) != PROP_TYPE_ARRAY) {
253 1.10 thorpej printf("boot: invalid %s\n", DT_OVERLAYS_PROP);
254 1.10 thorpej return;
255 1.10 thorpej }
256 1.10 thorpej
257 1.10 thorpej prop_object_iterator_t iter = prop_array_iterator(overlays);
258 1.10 thorpej prop_string_t pathobj;
259 1.10 thorpej while ((pathobj = prop_object_iterator_next(iter)) != NULL) {
260 1.10 thorpej if (prop_object_type(pathobj) != PROP_TYPE_STRING) {
261 1.10 thorpej printf("boot: invalid %s entry\n", DT_OVERLAYS_PROP);
262 1.10 thorpej continue;
263 1.10 thorpej }
264 1.10 thorpej apply_overlay_file(prop_string_cstring_nocopy(pathobj));
265 1.10 thorpej }
266 1.10 thorpej prop_object_iterator_release(iter);
267 1.10 thorpej }
268 1.10 thorpej
269 1.1 jmcneill int
270 1.1 jmcneill exec_netbsd(const char *fname, const char *args)
271 1.1 jmcneill {
272 1.1 jmcneill EFI_PHYSICAL_ADDRESS addr;
273 1.1 jmcneill u_long marks[MARK_MAX], alloc_size;
274 1.1 jmcneill EFI_STATUS status;
275 1.6 jmcneill int fd, ohowto;
276 1.1 jmcneill
277 1.10 thorpej load_file(get_initrd_path(), 0, false, &initrd_addr, &initrd_size);
278 1.10 thorpej load_file(get_dtb_path(), 0, false, &dtb_addr, &dtb_size);
279 1.4 jmcneill
280 1.1 jmcneill memset(marks, 0, sizeof(marks));
281 1.6 jmcneill ohowto = howto;
282 1.6 jmcneill howto |= AB_SILENT;
283 1.1 jmcneill fd = loadfile(fname, marks, COUNT_KERNEL | LOAD_NOTE);
284 1.6 jmcneill howto = ohowto;
285 1.1 jmcneill if (fd < 0) {
286 1.1 jmcneill printf("boot: %s: %s\n", fname, strerror(errno));
287 1.1 jmcneill return EIO;
288 1.1 jmcneill }
289 1.1 jmcneill close(fd);
290 1.1 jmcneill marks[MARK_END] = (((u_long) marks[MARK_END] + sizeof(int) - 1)) & (-sizeof(int));
291 1.4 jmcneill alloc_size = marks[MARK_END] - marks[MARK_START] + FDT_SPACE + EFIBOOT_ALIGN;
292 1.1 jmcneill
293 1.1 jmcneill #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
294 1.1 jmcneill addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
295 1.1 jmcneill status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
296 1.1 jmcneill EFI_SIZE_TO_PAGES(alloc_size), &addr);
297 1.1 jmcneill #else
298 1.1 jmcneill addr = 0;
299 1.1 jmcneill status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
300 1.1 jmcneill EFI_SIZE_TO_PAGES(alloc_size), &addr);
301 1.1 jmcneill #endif
302 1.1 jmcneill if (EFI_ERROR(status)) {
303 1.1 jmcneill printf("Failed to allocate %lu bytes for kernel image (error %lu)\n",
304 1.9 jmcneill alloc_size, (u_long)status);
305 1.1 jmcneill return ENOMEM;
306 1.1 jmcneill }
307 1.1 jmcneill
308 1.1 jmcneill memset(marks, 0, sizeof(marks));
309 1.3 jmcneill load_offset = (addr + EFIBOOT_ALIGN) & ~(EFIBOOT_ALIGN - 1);
310 1.1 jmcneill fd = loadfile(fname, marks, LOAD_KERNEL);
311 1.1 jmcneill if (fd < 0) {
312 1.1 jmcneill printf("boot: %s: %s\n", fname, strerror(errno));
313 1.1 jmcneill goto cleanup;
314 1.1 jmcneill }
315 1.1 jmcneill close(fd);
316 1.3 jmcneill load_offset = 0;
317 1.1 jmcneill
318 1.7 jmcneill #ifdef EFIBOOT_ACPI
319 1.7 jmcneill if (efi_acpi_available()) {
320 1.7 jmcneill efi_acpi_create_fdt();
321 1.7 jmcneill } else
322 1.7 jmcneill #endif
323 1.9 jmcneill if (dtb_addr && efi_fdt_set_data((void *)(uintptr_t)dtb_addr) != 0) {
324 1.5 jmcneill printf("boot: invalid DTB data\n");
325 1.5 jmcneill goto cleanup;
326 1.5 jmcneill }
327 1.5 jmcneill
328 1.1 jmcneill if (efi_fdt_size() > 0) {
329 1.4 jmcneill efi_fdt_init((marks[MARK_END] + FDT_ALIGN) & ~FDT_ALIGN, FDT_ALIGN + 1);
330 1.10 thorpej load_fdt_overlays();
331 1.4 jmcneill efi_fdt_initrd(initrd_addr, initrd_size);
332 1.2 jmcneill efi_fdt_bootargs(args);
333 1.4 jmcneill efi_fdt_memory_map();
334 1.8 jmcneill }
335 1.8 jmcneill
336 1.8 jmcneill efi_cleanup();
337 1.8 jmcneill
338 1.8 jmcneill if (efi_fdt_size() > 0) {
339 1.4 jmcneill efi_fdt_fini();
340 1.1 jmcneill }
341 1.1 jmcneill
342 1.1 jmcneill efi_boot_kernel(marks);
343 1.1 jmcneill
344 1.1 jmcneill /* This should not happen.. */
345 1.1 jmcneill printf("boot returned\n");
346 1.1 jmcneill
347 1.1 jmcneill cleanup:
348 1.1 jmcneill uefi_call_wrapper(BS->FreePages, 2, addr, EFI_SIZE_TO_PAGES(alloc_size));
349 1.4 jmcneill if (initrd_addr) {
350 1.4 jmcneill uefi_call_wrapper(BS->FreePages, 2, initrd_addr, EFI_SIZE_TO_PAGES(initrd_size));
351 1.4 jmcneill initrd_addr = 0;
352 1.4 jmcneill initrd_size = 0;
353 1.4 jmcneill }
354 1.5 jmcneill if (dtb_addr) {
355 1.5 jmcneill uefi_call_wrapper(BS->FreePages, 2, dtb_addr, EFI_SIZE_TO_PAGES(dtb_size));
356 1.5 jmcneill dtb_addr = 0;
357 1.5 jmcneill dtb_size = 0;
358 1.5 jmcneill }
359 1.1 jmcneill return EIO;
360 1.1 jmcneill }
361