Annotation of src/sys/stand/efiboot/exec.c, Revision 1.10.2.3
1.10.2.3! martin 1: /* $NetBSD$ */
1.10.2.2 christos 2:
3: /*-
4: * Copyright (c) 2019 Jason R. Thorpe
5: * Copyright (c) 2018 Jared McNeill <jmcneill@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:
1.10.2.3! martin 37: extern char twiddle_toggle;
! 38:
1.10.2.2 christos 39: u_long load_offset = 0;
40:
41: #define FDT_SPACE (4 * 1024 * 1024)
42: #define FDT_ALIGN ((2 * 1024 * 1024) - 1)
43:
1.10.2.3! martin 44: static EFI_PHYSICAL_ADDRESS initrd_addr, dtb_addr, rndseed_addr;
! 45: static u_long initrd_size = 0, dtb_size = 0, rndseed_size = 0;
1.10.2.2 christos 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;
1.10.2.3! martin 132: bool old_twiddle_toggle = twiddle_toggle;
1.10.2.2 christos 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:
1.10.2.3! martin 142: twiddle_toggle = load_quietly;
! 143:
1.10.2.2 christos 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:
1.10.2.3! martin 165: twiddle_toggle = old_twiddle_toggle;
! 166:
1.10.2.2 christos 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) {
1.10.2.3! martin 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:
1.10.2.2 christos 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);
1.10.2.3! martin 348: efi_fdt_rndseed(rndseed_addr, rndseed_size);
1.10.2.2 christos 349: efi_fdt_bootargs(args);
350: efi_fdt_memory_map();
351: }
352:
353: efi_cleanup();
354:
355: if (efi_fdt_size() > 0) {
356: efi_fdt_fini();
357: }
358:
359: efi_boot_kernel(marks);
360:
361: /* This should not happen.. */
362: printf("boot returned\n");
363:
364: cleanup:
365: uefi_call_wrapper(BS->FreePages, 2, addr, EFI_SIZE_TO_PAGES(alloc_size));
366: if (initrd_addr) {
367: uefi_call_wrapper(BS->FreePages, 2, initrd_addr, EFI_SIZE_TO_PAGES(initrd_size));
368: initrd_addr = 0;
369: initrd_size = 0;
370: }
371: if (dtb_addr) {
372: uefi_call_wrapper(BS->FreePages, 2, dtb_addr, EFI_SIZE_TO_PAGES(dtb_size));
373: dtb_addr = 0;
374: dtb_size = 0;
375: }
376: return EIO;
377: }
CVSweb <webmaster@jp.NetBSD.org>