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