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