xref: /netbsd-src/sys/stand/efiboot/exec.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /* $NetBSD: exec.c,v 1.21 2021/05/21 21:53:15 jmcneill Exp $ */
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 "efifdt.h"
32 #include "efiacpi.h"
33 #include "efirng.h"
34 #include "module.h"
35 #include "overlay.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)
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 void
126 apply_overlay(const char *path, void *dtbo)
127 {
128 
129 	if (!efi_fdt_overlay_is_compatible(dtbo)) {
130 		printf("boot: %s: incompatible overlay\n", path);
131 		return;
132 	}
133 
134 	int fdterr;
135 
136 	if (efi_fdt_overlay_apply(dtbo, &fdterr) != 0) {
137 		printf("boot: %s: error %d applying overlay\n", path, fdterr);
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 
156 	apply_overlay(path, (void *)(uintptr_t)dtbo_addr);
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 {
168 	if (!dtoverlay_enabled)
169 		return;
170 
171 	dtoverlay_foreach(apply_overlay_file);
172 }
173 
174 static void
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
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 
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;
247 	int fd, ohowto;
248 
249 	load_file(get_initrd_path(), 0, false, &initrd_addr, &initrd_size);
250 	load_file(get_dtb_path(), 0, false, &dtb_addr, &dtb_size);
251 	generate_efirng();
252 
253 	memset(marks, 0, sizeof(marks));
254 	ohowto = howto;
255 	howto |= AB_SILENT;
256 	fd = loadfile(fname, marks, COUNT_KERNEL | LOAD_NOTE);
257 	howto = ohowto;
258 	if (fd < 0) {
259 		printf("boot: %s: %s\n", fname, strerror(errno));
260 		return EIO;
261 	}
262 	close(fd);
263 	marks[MARK_END] = (((u_long) marks[MARK_END] + sizeof(int) - 1)) & -sizeof(int);
264 	alloc_size = marks[MARK_END] - marks[MARK_START] + FDT_SPACE + EFIBOOT_ALIGN;
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",
277 		    alloc_size, (u_long)status);
278 		return ENOMEM;
279 	}
280 
281 	memset(marks, 0, sizeof(marks));
282 	load_offset = (addr + EFIBOOT_ALIGN - 1) & -EFIBOOT_ALIGN;
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);
289 	load_offset = 0;
290 
291 #ifdef EFIBOOT_ACPI
292 	/* ACPI support only works for little endian kernels */
293 	efi_acpi_enable(netbsd_elf_data == ELFDATA2LSB);
294 
295 	if (efi_acpi_available() && efi_acpi_enabled()) {
296 		efi_acpi_create_fdt();
297 	} else
298 #endif
299 	if (dtb_addr && efi_fdt_set_data((void *)(uintptr_t)dtb_addr) != 0) {
300 		printf("boot: invalid DTB data\n");
301 		goto cleanup;
302 	}
303 
304 	if (efi_fdt_size() > 0) {
305 		/*
306 		 * Load the rndseed as late as possible -- after we
307 		 * have committed to using fdt and executing this
308 		 * kernel -- so that it doesn't hang around in memory
309 		 * if we have to bail or the kernel won't use it.
310 		 */
311 		load_file(get_rndseed_path(), 0, false,
312 		    &rndseed_addr, &rndseed_size);
313 
314 		efi_fdt_init((marks[MARK_END] + FDT_ALIGN - 1) & -FDT_ALIGN, FDT_ALIGN);
315 		load_modules(fname);
316 		load_fdt_overlays();
317 		efi_fdt_initrd(initrd_addr, initrd_size);
318 		efi_fdt_rndseed(rndseed_addr, rndseed_size);
319 		efi_fdt_efirng(efirng_addr, efirng_size);
320 		efi_fdt_bootargs(args);
321 		efi_fdt_system_table();
322 		efi_fdt_gop();
323 		efi_fdt_memory_map();
324 	}
325 
326 	efi_cleanup();
327 
328 	if (efi_fdt_size() > 0) {
329 		efi_fdt_fini();
330 	}
331 
332 	efi_boot_kernel(marks);
333 
334 	/* This should not happen.. */
335 	printf("boot returned\n");
336 
337 cleanup:
338 	uefi_call_wrapper(BS->FreePages, 2, addr, EFI_SIZE_TO_PAGES(alloc_size));
339 	if (initrd_addr) {
340 		uefi_call_wrapper(BS->FreePages, 2, initrd_addr, EFI_SIZE_TO_PAGES(initrd_size));
341 		initrd_addr = 0;
342 		initrd_size = 0;
343 	}
344 	if (dtb_addr) {
345 		uefi_call_wrapper(BS->FreePages, 2, dtb_addr, EFI_SIZE_TO_PAGES(dtb_size));
346 		dtb_addr = 0;
347 		dtb_size = 0;
348 	}
349 	return EIO;
350 }
351