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