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