exec.c revision 1.26 1 1.26 mlelstv /* $NetBSD: exec.c,v 1.26 2024/09/19 06:26:11 mlelstv Exp $ */
2 1.1 jmcneill
3 1.1 jmcneill /*-
4 1.10 thorpej * Copyright (c) 2019 Jason R. Thorpe
5 1.1 jmcneill * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
6 1.1 jmcneill * All rights reserved.
7 1.1 jmcneill *
8 1.1 jmcneill * Redistribution and use in source and binary forms, with or without
9 1.1 jmcneill * modification, are permitted provided that the following conditions
10 1.1 jmcneill * are met:
11 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright
12 1.1 jmcneill * notice, this list of conditions and the following disclaimer.
13 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the
15 1.1 jmcneill * documentation and/or other materials provided with the distribution.
16 1.1 jmcneill *
17 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 1.1 jmcneill * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 1.1 jmcneill * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 1.1 jmcneill * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 1.1 jmcneill * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 1.1 jmcneill * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 1.1 jmcneill * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 1.1 jmcneill * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 1.1 jmcneill * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 1.1 jmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 1.1 jmcneill * SUCH DAMAGE.
28 1.1 jmcneill */
29 1.1 jmcneill
30 1.1 jmcneill #include "efiboot.h"
31 1.23 jmcneill #ifdef EFIBOOT_FDT
32 1.1 jmcneill #include "efifdt.h"
33 1.23 jmcneill #endif
34 1.23 jmcneill #ifdef EFIBOOT_ACPI
35 1.7 jmcneill #include "efiacpi.h"
36 1.23 jmcneill #endif
37 1.14 riastrad #include "efirng.h"
38 1.16 jmcneill #include "module.h"
39 1.1 jmcneill
40 1.16 jmcneill #include <sys/param.h>
41 1.6 jmcneill #include <sys/reboot.h>
42 1.1 jmcneill
43 1.13 jmcneill extern char twiddle_toggle;
44 1.13 jmcneill
45 1.3 jmcneill u_long load_offset = 0;
46 1.3 jmcneill
47 1.23 jmcneill EFI_PHYSICAL_ADDRESS efirng_addr;
48 1.23 jmcneill u_long efirng_size = 0;
49 1.4 jmcneill
50 1.23 jmcneill int
51 1.10 thorpej load_file(const char *path, u_long extra, bool quiet_errors,
52 1.10 thorpej EFI_PHYSICAL_ADDRESS *paddr, u_long *psize)
53 1.4 jmcneill {
54 1.4 jmcneill EFI_STATUS status;
55 1.4 jmcneill struct stat st;
56 1.4 jmcneill ssize_t len;
57 1.10 thorpej ssize_t expectedlen;
58 1.4 jmcneill int fd;
59 1.4 jmcneill
60 1.4 jmcneill if (strlen(path) == 0)
61 1.4 jmcneill return 0;
62 1.4 jmcneill
63 1.4 jmcneill fd = open(path, 0);
64 1.4 jmcneill if (fd < 0) {
65 1.10 thorpej if (!quiet_errors) {
66 1.10 thorpej printf("boot: failed to open %s: %s\n", path,
67 1.10 thorpej strerror(errno));
68 1.10 thorpej }
69 1.4 jmcneill return errno;
70 1.4 jmcneill }
71 1.4 jmcneill if (fstat(fd, &st) < 0) {
72 1.4 jmcneill printf("boot: failed to fstat %s: %s\n", path, strerror(errno));
73 1.4 jmcneill close(fd);
74 1.4 jmcneill return errno;
75 1.4 jmcneill }
76 1.4 jmcneill if (st.st_size == 0) {
77 1.10 thorpej if (!quiet_errors) {
78 1.10 thorpej printf("boot: empty file %s\n", path);
79 1.10 thorpej }
80 1.4 jmcneill close(fd);
81 1.4 jmcneill return EINVAL;
82 1.4 jmcneill }
83 1.4 jmcneill
84 1.10 thorpej expectedlen = st.st_size;
85 1.10 thorpej *psize = st.st_size + extra;
86 1.4 jmcneill
87 1.4 jmcneill #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
88 1.5 jmcneill *paddr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
89 1.4 jmcneill status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
90 1.5 jmcneill EFI_SIZE_TO_PAGES(*psize), paddr);
91 1.4 jmcneill #else
92 1.5 jmcneill *paddr = 0;
93 1.4 jmcneill status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
94 1.5 jmcneill EFI_SIZE_TO_PAGES(*psize), paddr);
95 1.4 jmcneill #endif
96 1.4 jmcneill if (EFI_ERROR(status)) {
97 1.5 jmcneill printf("Failed to allocate %lu bytes for %s (error %lu)\n",
98 1.9 jmcneill *psize, path, (u_long)status);
99 1.4 jmcneill close(fd);
100 1.10 thorpej *paddr = 0;
101 1.4 jmcneill return ENOMEM;
102 1.4 jmcneill }
103 1.4 jmcneill
104 1.4 jmcneill printf("boot: loading %s ", path);
105 1.10 thorpej len = read(fd, (void *)(uintptr_t)*paddr, expectedlen);
106 1.4 jmcneill close(fd);
107 1.4 jmcneill
108 1.10 thorpej if (len != expectedlen) {
109 1.10 thorpej if (len < 0) {
110 1.4 jmcneill printf(": %s\n", strerror(errno));
111 1.10 thorpej } else {
112 1.24 rin printf(": returned %zd (expected %zd)\n", len,
113 1.10 thorpej expectedlen);
114 1.10 thorpej }
115 1.4 jmcneill return EIO;
116 1.4 jmcneill }
117 1.4 jmcneill
118 1.4 jmcneill printf("done.\n");
119 1.4 jmcneill
120 1.5 jmcneill efi_dcache_flush(*paddr, *psize);
121 1.4 jmcneill
122 1.4 jmcneill return 0;
123 1.4 jmcneill }
124 1.4 jmcneill
125 1.10 thorpej static void
126 1.14 riastrad generate_efirng(void)
127 1.14 riastrad {
128 1.14 riastrad EFI_PHYSICAL_ADDRESS addr;
129 1.14 riastrad u_long size = EFI_PAGE_SIZE;
130 1.14 riastrad EFI_STATUS status;
131 1.14 riastrad
132 1.14 riastrad /* Check whether the RNG is available before bothering. */
133 1.14 riastrad if (!efi_rng_available())
134 1.14 riastrad return;
135 1.14 riastrad
136 1.14 riastrad /*
137 1.14 riastrad * Allocate a page. This is the smallest unit we can pass into
138 1.14 riastrad * the kernel conveniently.
139 1.14 riastrad */
140 1.14 riastrad #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
141 1.14 riastrad addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
142 1.14 riastrad status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress,
143 1.14 riastrad EfiLoaderData, EFI_SIZE_TO_PAGES(size), &addr);
144 1.14 riastrad #else
145 1.14 riastrad addr = 0;
146 1.14 riastrad status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages,
147 1.14 riastrad EfiLoaderData, EFI_SIZE_TO_PAGES(size), &addr);
148 1.14 riastrad #endif
149 1.14 riastrad if (EFI_ERROR(status)) {
150 1.14 riastrad Print(L"Failed to allocate page for EFI RNG output: %r\n",
151 1.14 riastrad status);
152 1.14 riastrad return;
153 1.14 riastrad }
154 1.14 riastrad
155 1.14 riastrad /* Fill the page with whatever the EFI RNG will do. */
156 1.14 riastrad if (efi_rng((void *)(uintptr_t)addr, size)) {
157 1.26 mlelstv uefi_call_wrapper(BS->FreePages, 2, addr,
158 1.26 mlelstv EFI_SIZE_TO_PAGES(size));
159 1.14 riastrad return;
160 1.14 riastrad }
161 1.14 riastrad
162 1.14 riastrad /* Success! */
163 1.14 riastrad efirng_addr = addr;
164 1.14 riastrad efirng_size = size;
165 1.14 riastrad }
166 1.14 riastrad
167 1.1 jmcneill int
168 1.1 jmcneill exec_netbsd(const char *fname, const char *args)
169 1.1 jmcneill {
170 1.1 jmcneill EFI_PHYSICAL_ADDRESS addr;
171 1.1 jmcneill u_long marks[MARK_MAX], alloc_size;
172 1.1 jmcneill EFI_STATUS status;
173 1.6 jmcneill int fd, ohowto;
174 1.1 jmcneill
175 1.22 jmcneill twiddle_toggle = 0;
176 1.22 jmcneill
177 1.14 riastrad generate_efirng();
178 1.4 jmcneill
179 1.1 jmcneill memset(marks, 0, sizeof(marks));
180 1.6 jmcneill ohowto = howto;
181 1.6 jmcneill howto |= AB_SILENT;
182 1.1 jmcneill fd = loadfile(fname, marks, COUNT_KERNEL | LOAD_NOTE);
183 1.6 jmcneill howto = ohowto;
184 1.1 jmcneill if (fd < 0) {
185 1.1 jmcneill printf("boot: %s: %s\n", fname, strerror(errno));
186 1.1 jmcneill return EIO;
187 1.1 jmcneill }
188 1.1 jmcneill close(fd);
189 1.20 skrll marks[MARK_END] = (((u_long) marks[MARK_END] + sizeof(int) - 1)) & -sizeof(int);
190 1.25 skrll alloc_size = marks[MARK_END] - marks[MARK_START] + efi_fdt_alloc_size() + EFIBOOT_ALIGN;
191 1.1 jmcneill
192 1.1 jmcneill #ifdef EFIBOOT_ALLOCATE_MAX_ADDRESS
193 1.1 jmcneill addr = EFIBOOT_ALLOCATE_MAX_ADDRESS;
194 1.1 jmcneill status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
195 1.1 jmcneill EFI_SIZE_TO_PAGES(alloc_size), &addr);
196 1.1 jmcneill #else
197 1.1 jmcneill addr = 0;
198 1.1 jmcneill status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiLoaderData,
199 1.1 jmcneill EFI_SIZE_TO_PAGES(alloc_size), &addr);
200 1.1 jmcneill #endif
201 1.1 jmcneill if (EFI_ERROR(status)) {
202 1.1 jmcneill printf("Failed to allocate %lu bytes for kernel image (error %lu)\n",
203 1.9 jmcneill alloc_size, (u_long)status);
204 1.1 jmcneill return ENOMEM;
205 1.1 jmcneill }
206 1.1 jmcneill
207 1.1 jmcneill memset(marks, 0, sizeof(marks));
208 1.20 skrll load_offset = (addr + EFIBOOT_ALIGN - 1) & -EFIBOOT_ALIGN;
209 1.1 jmcneill fd = loadfile(fname, marks, LOAD_KERNEL);
210 1.1 jmcneill if (fd < 0) {
211 1.1 jmcneill printf("boot: %s: %s\n", fname, strerror(errno));
212 1.1 jmcneill goto cleanup;
213 1.1 jmcneill }
214 1.1 jmcneill close(fd);
215 1.3 jmcneill load_offset = 0;
216 1.1 jmcneill
217 1.25 skrll if (efi_fdt_prepare_boot(fname, args, marks) != 0) {
218 1.5 jmcneill goto cleanup;
219 1.5 jmcneill }
220 1.5 jmcneill
221 1.1 jmcneill efi_boot_kernel(marks);
222 1.1 jmcneill
223 1.1 jmcneill /* This should not happen.. */
224 1.1 jmcneill printf("boot returned\n");
225 1.1 jmcneill
226 1.1 jmcneill cleanup:
227 1.1 jmcneill uefi_call_wrapper(BS->FreePages, 2, addr, EFI_SIZE_TO_PAGES(alloc_size));
228 1.25 skrll efi_fdt_cleanup_boot();
229 1.23 jmcneill
230 1.1 jmcneill return EIO;
231 1.1 jmcneill }
232