Annotation of src/sys/stand/efiboot/exec.c, Revision 1.20
1.20 ! skrll 1: /* $NetBSD: exec.c,v 1.19 2020/10/10 19:17:39 jmcneill Exp $ */
1.1 jmcneill 2:
3: /*-
1.10 thorpej 4: * Copyright (c) 2019 Jason R. Thorpe
1.1 jmcneill 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 "efifdt.h"
1.7 jmcneill 32: #include "efiacpi.h"
1.14 riastrad 33: #include "efirng.h"
1.16 jmcneill 34: #include "module.h"
1.17 thorpej 35: #include "overlay.h"
1.1 jmcneill 36:
1.16 jmcneill 37: #include <sys/param.h>
1.6 jmcneill 38: #include <sys/reboot.h>
1.1 jmcneill 39:
1.13 jmcneill 40: extern char twiddle_toggle;
41:
1.3 jmcneill 42: u_long load_offset = 0;
43:
1.4 jmcneill 44: #define FDT_SPACE (4 * 1024 * 1024)
1.20 ! skrll 45: #define FDT_ALIGN (2 * 1024 * 1024)
1.4 jmcneill 46:
1.14 riastrad 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;
1.4 jmcneill 49:
50: static int
1.10 thorpej 51: load_file(const char *path, u_long extra, bool quiet_errors,
52: EFI_PHYSICAL_ADDRESS *paddr, u_long *psize)
1.4 jmcneill 53: {
54: EFI_STATUS status;
55: struct stat st;
56: ssize_t len;
1.10 thorpej 57: ssize_t expectedlen;
1.4 jmcneill 58: int fd;
59:
60: if (strlen(path) == 0)
61: return 0;
62:
63: fd = open(path, 0);
64: if (fd < 0) {
1.10 thorpej 65: if (!quiet_errors) {
66: printf("boot: failed to open %s: %s\n", path,
67: strerror(errno));
68: }
1.4 jmcneill 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) {
1.10 thorpej 77: if (!quiet_errors) {
78: printf("boot: empty file %s\n", path);
79: }
1.4 jmcneill 80: close(fd);
81: return EINVAL;
82: }
83:
1.10 thorpej 84: expectedlen = st.st_size;
85: *psize = st.st_size + extra;
1.4 jmcneill 86:
87: #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
1.5 jmcneill 88: *paddr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
1.4 jmcneill 89: status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
1.5 jmcneill 90: EFI_SIZE_TO_PAGES(*psize), paddr);
1.4 jmcneill 91: #else
1.5 jmcneill 92: *paddr = 0;
1.4 jmcneill 93: status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
1.5 jmcneill 94: EFI_SIZE_TO_PAGES(*psize), paddr);
1.4 jmcneill 95: #endif
96: if (EFI_ERROR(status)) {
1.5 jmcneill 97: printf("Failed to allocate %lu bytes for %s (error %lu)\n",
1.9 jmcneill 98: *psize, path, (u_long)status);
1.4 jmcneill 99: close(fd);
1.10 thorpej 100: *paddr = 0;
1.4 jmcneill 101: return ENOMEM;
102: }
103:
104: printf("boot: loading %s ", path);
1.10 thorpej 105: len = read(fd, (void *)(uintptr_t)*paddr, expectedlen);
1.4 jmcneill 106: close(fd);
107:
1.10 thorpej 108: if (len != expectedlen) {
109: if (len < 0) {
1.4 jmcneill 110: printf(": %s\n", strerror(errno));
1.10 thorpej 111: } else {
112: printf(": returned %ld (expected %ld)\n", len,
113: expectedlen);
114: }
1.4 jmcneill 115: return EIO;
116: }
117:
118: printf("done.\n");
119:
1.5 jmcneill 120: efi_dcache_flush(*paddr, *psize);
1.4 jmcneill 121:
122: return 0;
123: }
124:
1.10 thorpej 125: static void
1.17 thorpej 126: apply_overlay(const char *path, void *dtbo)
1.10 thorpej 127: {
128:
129: if (!efi_fdt_overlay_is_compatible(dtbo)) {
1.17 thorpej 130: printf("boot: %s: incompatible overlay\n", path);
1.15 thorpej 131: return;
1.10 thorpej 132: }
133:
134: int fdterr;
135:
136: if (efi_fdt_overlay_apply(dtbo, &fdterr) != 0) {
1.17 thorpej 137: printf("boot: %s: error %d applying overlay\n", path, fdterr);
1.10 thorpej 138: }
139: }
140:
141: static void
142: apply_overlay_file(const char *path)
143: {
144: EFI_PHYSICAL_ADDRESS dtbo_addr;
145: u_long dtbo_size;
146:
147: if (strlen(path) == 0)
148: return;
149:
150: if (load_file(path, 0, false, &dtbo_addr, &dtbo_size) != 0 ||
151: dtbo_addr == 0) {
152: /* Error messages have already been displayed. */
153: goto out;
154: }
155:
1.17 thorpej 156: apply_overlay(path, (void *)(uintptr_t)dtbo_addr);
1.10 thorpej 157:
158: out:
159: if (dtbo_addr) {
160: uefi_call_wrapper(BS->FreePages, 2, dtbo_addr,
161: EFI_SIZE_TO_PAGES(dtbo_size));
162: }
163: }
164:
165: static void
166: load_fdt_overlays(void)
167: {
1.17 thorpej 168: if (!dtoverlay_enabled)
1.10 thorpej 169: return;
170:
1.17 thorpej 171: dtoverlay_foreach(apply_overlay_file);
1.10 thorpej 172: }
173:
1.14 riastrad 174: static void
1.16 jmcneill 175: load_module(const char *module_name)
176: {
177: EFI_PHYSICAL_ADDRESS addr;
178: u_long size;
179: char path[PATH_MAX];
180:
181: snprintf(path, sizeof(path), "%s/%s/%s.kmod", module_prefix,
182: module_name, module_name);
183:
184: if (load_file(path, 0, false, &addr, &size) != 0 || addr == 0 || size == 0)
185: return;
186:
187: efi_fdt_module(module_name, (u_long)addr, size);
188: }
189:
190: static void
191: load_modules(const char *kernel_name)
192: {
193: if (!module_enabled)
194: return;
195:
196: module_init(kernel_name);
197: module_foreach(load_module);
198: }
199:
200: static void
1.14 riastrad 201: generate_efirng(void)
202: {
203: EFI_PHYSICAL_ADDRESS addr;
204: u_long size = EFI_PAGE_SIZE;
205: EFI_STATUS status;
206:
207: /* Check whether the RNG is available before bothering. */
208: if (!efi_rng_available())
209: return;
210:
211: /*
212: * Allocate a page. This is the smallest unit we can pass into
213: * the kernel conveniently.
214: */
215: #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
216: addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
217: status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress,
218: EfiLoaderData, EFI_SIZE_TO_PAGES(size), &addr);
219: #else
220: addr = 0;
221: status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages,
222: EfiLoaderData, EFI_SIZE_TO_PAGES(size), &addr);
223: #endif
224: if (EFI_ERROR(status)) {
225: Print(L"Failed to allocate page for EFI RNG output: %r\n",
226: status);
227: return;
228: }
229:
230: /* Fill the page with whatever the EFI RNG will do. */
231: if (efi_rng((void *)(uintptr_t)addr, size)) {
232: uefi_call_wrapper(BS->FreePages, 2, addr, size);
233: return;
234: }
235:
236: /* Success! */
237: efirng_addr = addr;
238: efirng_size = size;
239: }
240:
1.1 jmcneill 241: int
242: exec_netbsd(const char *fname, const char *args)
243: {
244: EFI_PHYSICAL_ADDRESS addr;
245: u_long marks[MARK_MAX], alloc_size;
246: EFI_STATUS status;
1.6 jmcneill 247: int fd, ohowto;
1.1 jmcneill 248:
1.10 thorpej 249: load_file(get_initrd_path(), 0, false, &initrd_addr, &initrd_size);
250: load_file(get_dtb_path(), 0, false, &dtb_addr, &dtb_size);
1.14 riastrad 251: generate_efirng();
1.4 jmcneill 252:
1.1 jmcneill 253: memset(marks, 0, sizeof(marks));
1.6 jmcneill 254: ohowto = howto;
255: howto |= AB_SILENT;
1.1 jmcneill 256: fd = loadfile(fname, marks, COUNT_KERNEL | LOAD_NOTE);
1.6 jmcneill 257: howto = ohowto;
1.1 jmcneill 258: if (fd < 0) {
259: printf("boot: %s: %s\n", fname, strerror(errno));
260: return EIO;
261: }
262: close(fd);
1.20 ! skrll 263: marks[MARK_END] = (((u_long) marks[MARK_END] + sizeof(int) - 1)) & -sizeof(int);
1.4 jmcneill 264: alloc_size = marks[MARK_END] - marks[MARK_START] + FDT_SPACE + EFIBOOT_ALIGN;
1.1 jmcneill 265:
266: #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
267: addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
268: status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
269: EFI_SIZE_TO_PAGES(alloc_size), &addr);
270: #else
271: addr = 0;
272: status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
273: EFI_SIZE_TO_PAGES(alloc_size), &addr);
274: #endif
275: if (EFI_ERROR(status)) {
276: printf("Failed to allocate %lu bytes for kernel image (error %lu)\n",
1.9 jmcneill 277: alloc_size, (u_long)status);
1.1 jmcneill 278: return ENOMEM;
279: }
280:
281: memset(marks, 0, sizeof(marks));
1.20 ! skrll 282: load_offset = (addr + EFIBOOT_ALIGN - 1) & -EFIBOOT_ALIGN;
1.1 jmcneill 283: fd = loadfile(fname, marks, LOAD_KERNEL);
284: if (fd < 0) {
285: printf("boot: %s: %s\n", fname, strerror(errno));
286: goto cleanup;
287: }
288: close(fd);
1.3 jmcneill 289: load_offset = 0;
1.1 jmcneill 290:
1.7 jmcneill 291: #ifdef EFIBOOT_ACPI
292: if (efi_acpi_available()) {
293: efi_acpi_create_fdt();
294: } else
295: #endif
1.9 jmcneill 296: if (dtb_addr && efi_fdt_set_data((void *)(uintptr_t)dtb_addr) != 0) {
1.5 jmcneill 297: printf("boot: invalid DTB data\n");
298: goto cleanup;
299: }
300:
1.1 jmcneill 301: if (efi_fdt_size() > 0) {
1.12 riastrad 302: /*
303: * Load the rndseed as late as possible -- after we
304: * have committed to using fdt and executing this
305: * kernel -- so that it doesn't hang around in memory
306: * if we have to bail or the kernel won't use it.
307: */
308: load_file(get_rndseed_path(), 0, false,
309: &rndseed_addr, &rndseed_size);
310:
1.20 ! skrll 311: efi_fdt_init((marks[MARK_END] + FDT_ALIGN - 1) & -FDT_ALIGN, FDT_ALIGN);
1.16 jmcneill 312: load_modules(fname);
1.10 thorpej 313: load_fdt_overlays();
1.4 jmcneill 314: efi_fdt_initrd(initrd_addr, initrd_size);
1.12 riastrad 315: efi_fdt_rndseed(rndseed_addr, rndseed_size);
1.14 riastrad 316: efi_fdt_efirng(efirng_addr, efirng_size);
1.2 jmcneill 317: efi_fdt_bootargs(args);
1.19 jmcneill 318: efi_fdt_system_table();
319: efi_fdt_gop();
1.4 jmcneill 320: efi_fdt_memory_map();
1.8 jmcneill 321: }
322:
323: efi_cleanup();
324:
325: if (efi_fdt_size() > 0) {
1.4 jmcneill 326: efi_fdt_fini();
1.1 jmcneill 327: }
328:
329: efi_boot_kernel(marks);
330:
331: /* This should not happen.. */
332: printf("boot returned\n");
333:
334: cleanup:
335: uefi_call_wrapper(BS->FreePages, 2, addr, EFI_SIZE_TO_PAGES(alloc_size));
1.4 jmcneill 336: if (initrd_addr) {
337: uefi_call_wrapper(BS->FreePages, 2, initrd_addr, EFI_SIZE_TO_PAGES(initrd_size));
338: initrd_addr = 0;
339: initrd_size = 0;
340: }
1.5 jmcneill 341: if (dtb_addr) {
342: uefi_call_wrapper(BS->FreePages, 2, dtb_addr, EFI_SIZE_TO_PAGES(dtb_size));
343: dtb_addr = 0;
344: dtb_size = 0;
345: }
1.1 jmcneill 346: return EIO;
347: }
CVSweb <webmaster@jp.NetBSD.org>