fdt_boot.c revision 1.4 1 /* $NetBSD: fdt_boot.c,v 1.4 2023/07/11 05:57:44 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2015-2017 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*-
30 * Copyright (c) 2022 The NetBSD Foundation, Inc.
31 * All rights reserved.
32 *
33 * This code is derived from software contributed to The NetBSD Foundation
34 * by Nick Hudson
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
46 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
47 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
49 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
52 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
53 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
55 * POSSIBILITY OF SUCH DAMAGE.
56 */
57
58 #include <sys/cdefs.h>
59 __KERNEL_RCSID(0, "$NetBSD: fdt_boot.c,v 1.4 2023/07/11 05:57:44 skrll Exp $");
60
61 #include "opt_efi.h"
62 #include "opt_md.h"
63
64 #include <sys/param.h>
65
66 #include <sys/disk.h>
67 #include <sys/disklabel.h>
68 #include <sys/fcntl.h>
69 #include <sys/md5.h>
70 #include <sys/optstr.h>
71 #include <sys/rnd.h>
72 #include <sys/rndsource.h>
73 #include <sys/uuid.h>
74 #include <sys/vnode.h>
75
76 #include <net/if.h>
77 #include <net/if_dl.h>
78
79 #include <uvm/uvm_extern.h>
80
81 #include <libfdt.h>
82
83 #include <dev/fdt/fdtvar.h>
84 #include <dev/fdt/fdt_boot.h>
85 #include <dev/fdt/fdt_memory.h>
86
87 #ifndef FDT_MAX_BOOT_STRING
88 #define FDT_MAX_BOOT_STRING 1024
89 #endif
90 static char bootargs[FDT_MAX_BOOT_STRING] = "";
91
92 #ifdef EFI_RUNTIME
93 #include <machine/efirt.h>
94
95 void fdt_map_efi_runtime(const char *, enum cpu_efirt_mem_type);
96
97 #endif
98
99 #ifdef MEMORY_DISK_DYNAMIC
100 #include <dev/md.h>
101
102 static uint64_t initrd_start, initrd_end;
103 #endif
104
105 static uint64_t rndseed_start, rndseed_end; /* our on-disk seed */
106 static uint64_t efirng_start, efirng_end; /* firmware's EFI RNG output */
107 static struct krndsource efirng_source;
108
109
110 static void
111 fdt_probe_range(const char *startname, const char *endname,
112 uint64_t *pstart, uint64_t *pend)
113 {
114 int chosen, len;
115 const void *start_data, *end_data;
116
117 *pstart = *pend = 0;
118
119 chosen = OF_finddevice("/chosen");
120 if (chosen < 0)
121 return;
122
123 start_data = fdtbus_get_prop(chosen, startname, &len);
124 end_data = fdtbus_get_prop(chosen, endname, NULL);
125 if (start_data == NULL || end_data == NULL)
126 return;
127
128 switch (len) {
129 case 4:
130 *pstart = be32dec(start_data);
131 *pend = be32dec(end_data);
132 break;
133 case 8:
134 *pstart = be64dec(start_data);
135 *pend = be64dec(end_data);
136 break;
137 default:
138 printf("Unsupported len %d for /chosen `%s'\n",
139 len, startname);
140 return;
141 }
142 }
143
144
145 static void *
146 fdt_map_range(uint64_t start, uint64_t end, uint64_t *psize,
147 const char *purpose)
148 {
149 const paddr_t startpa = trunc_page(start);
150 const paddr_t endpa = round_page(end);
151 paddr_t pa;
152 vaddr_t va;
153 void *ptr;
154
155 *psize = end - start;
156 if (*psize == 0)
157 return NULL;
158
159 const vaddr_t voff = start & PAGE_MASK;
160
161 // XXX NH add an align so map_chunk works betterer?
162 va = uvm_km_alloc(kernel_map, *psize, 0, UVM_KMF_VAONLY | UVM_KMF_NOWAIT);
163 if (va == 0) {
164 printf("Failed to allocate VA for %s\n", purpose);
165 return NULL;
166 }
167 ptr = (void *)(va + voff);
168
169 // XXX NH map chunk
170 for (pa = startpa; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE)
171 pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0);
172 pmap_update(pmap_kernel());
173
174 return ptr;
175 }
176
177 static void
178 fdt_unmap_range(void *ptr, uint64_t size)
179 {
180 const char *start = ptr, *end = start + size;
181 const vaddr_t startva = trunc_page((vaddr_t)(uintptr_t)start);
182 const vaddr_t endva = round_page((vaddr_t)(uintptr_t)end);
183 const vsize_t sz = endva - startva;
184
185 pmap_kremove(startva, sz);
186 pmap_update(pmap_kernel());
187
188 uvm_km_free(kernel_map, startva, sz, UVM_KMF_VAONLY);
189 }
190
191 char *
192 fdt_get_bootargs(void)
193 {
194 const int chosen = OF_finddevice("/chosen");
195
196 if (chosen >= 0)
197 OF_getprop(chosen, "bootargs", bootargs, sizeof(bootargs));
198 return bootargs;
199 }
200
201 void
202 fdt_probe_initrd(void)
203 {
204
205 #ifdef MEMORY_DISK_DYNAMIC
206 fdt_probe_range("linux,initrd-start", "linux,initrd-end",
207 &initrd_start, &initrd_end);
208 #endif
209 }
210
211 void
212 fdt_setup_initrd(void)
213 {
214 #ifdef MEMORY_DISK_DYNAMIC
215 void *md_start;
216 uint64_t initrd_size;
217
218 md_start = fdt_map_range(initrd_start, initrd_end, &initrd_size,
219 "initrd");
220 if (md_start == NULL)
221 return;
222 md_root_setconf(md_start, initrd_size);
223 #endif
224 }
225
226 void
227 fdt_reserve_initrd(void)
228 {
229 #ifdef MEMORY_DISK_DYNAMIC
230 const uint64_t initrd_size =
231 round_page(initrd_end) - trunc_page(initrd_start);
232
233 if (initrd_size > 0)
234 fdt_memory_remove_range(trunc_page(initrd_start), initrd_size);
235 #endif
236 }
237
238 void
239 fdt_probe_rndseed(void)
240 {
241
242 fdt_probe_range("netbsd,rndseed-start", "netbsd,rndseed-end",
243 &rndseed_start, &rndseed_end);
244 }
245
246 void
247 fdt_setup_rndseed(void)
248 {
249 uint64_t rndseed_size;
250 void *rndseed;
251
252 rndseed = fdt_map_range(rndseed_start, rndseed_end, &rndseed_size,
253 "rndseed");
254 if (rndseed == NULL)
255 return;
256 rnd_seed(rndseed, rndseed_size);
257 fdt_unmap_range(rndseed, rndseed_size);
258 }
259
260 void
261 fdt_reserve_rndseed(void)
262 {
263 const uint64_t rndseed_size =
264 round_page(rndseed_end) - trunc_page(rndseed_start);
265
266 if (rndseed_size > 0)
267 fdt_memory_remove_range(trunc_page(rndseed_start),
268 rndseed_size);
269 }
270
271 void
272 fdt_probe_efirng(void)
273 {
274
275 fdt_probe_range("netbsd,efirng-start", "netbsd,efirng-end",
276 &efirng_start, &efirng_end);
277 }
278
279 void
280 fdt_setup_efirng(void)
281 {
282 uint64_t efirng_size;
283 void *efirng;
284
285 efirng = fdt_map_range(efirng_start, efirng_end, &efirng_size,
286 "efirng");
287 if (efirng == NULL)
288 return;
289
290 rnd_attach_source(&efirng_source, "efirng", RND_TYPE_RNG,
291 RND_FLAG_DEFAULT);
292
293 /*
294 * We don't really have specific information about the physical
295 * process underlying the data provided by the firmware via the
296 * EFI RNG API, so the entropy estimate here is heuristic.
297 * What efiboot provides us is up to 4096 bytes of data from
298 * the EFI RNG API, although in principle it may return short.
299 *
300 * The UEFI Specification (2.8 Errata A, February 2020[1]) says
301 *
302 * When a Deterministic Random Bit Generator (DRBG) is
303 * used on the output of a (raw) entropy source, its
304 * security level must be at least 256 bits.
305 *
306 * It's not entirely clear whether `it' refers to the DRBG or
307 * the entropy source; if it refers to the DRBG, it's not
308 * entirely clear how ANSI X9.31 3DES, one of the options for
309 * DRBG in the UEFI spec, can provide a `256-bit security
310 * level' because it has only 232 bits of inputs (three 56-bit
311 * keys and one 64-bit block). That said, even if it provides
312 * only 232 bits of entropy, that's enough to prevent all
313 * attacks and we probably get a few more bits from sampling
314 * the clock anyway.
315 *
316 * In the event we get raw samples, e.g. the bits sampled by a
317 * ring oscillator, we hope that the samples have at least half
318 * a bit of entropy per bit of data -- and efiboot tries to
319 * draw 4096 bytes to provide plenty of slop. Hence we divide
320 * the total number of bits by two and clamp at 256. There are
321 * ways this could go wrong, but on most machines it should
322 * behave reasonably.
323 *
324 * [1] https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf
325 */
326 rnd_add_data(&efirng_source, efirng, efirng_size,
327 MIN(256, efirng_size*NBBY/2));
328
329 explicit_memset(efirng, 0, efirng_size);
330 fdt_unmap_range(efirng, efirng_size);
331 }
332
333 void
334 fdt_reserve_efirng(void)
335 {
336 const uint64_t efirng_size =
337 round_page(efirng_end) - trunc_page(efirng_start);
338
339 if (efirng_size > 0)
340 fdt_memory_remove_range(trunc_page(efirng_start), efirng_size);
341 }
342
343 #ifdef EFI_RUNTIME
344 void
345 fdt_map_efi_runtime(const char *prop, enum cpu_efirt_mem_type type)
346 {
347 int len;
348
349 const int chosen_off = fdt_path_offset(fdtbus_get_data(), "/chosen");
350 if (chosen_off < 0)
351 return;
352
353 const uint64_t *map = fdt_getprop(fdtbus_get_data(), chosen_off, prop, &len);
354 if (map == NULL)
355 return;
356
357 while (len >= 24) {
358 const paddr_t pa = be64toh(map[0]);
359 const vaddr_t va = be64toh(map[1]);
360 const size_t sz = be64toh(map[2]);
361 #if 0
362 VPRINTF("%s: %s %#" PRIxPADDR "-%#" PRIxVADDR " (%#" PRIxVADDR
363 "-%#" PRIxVSIZE ")\n", __func__, prop, pa, pa + sz - 1,
364 va, va + sz - 1);
365 #endif
366 cpu_efirt_map_range(va, pa, sz, type);
367 map += 3;
368 len -= 24;
369 }
370 }
371 #endif
372
373 void
374 fdt_update_stdout_path(void *fdt, const char *boot_args)
375 {
376 const char *stdout_path;
377 char buf[256];
378
379 const int chosen_off = fdt_path_offset(fdt, "/chosen");
380 if (chosen_off == -1)
381 return;
382
383 if (optstr_get_string(boot_args, "stdout-path", &stdout_path) == false)
384 return;
385
386 const char *ep = strchr(stdout_path, ' ');
387 size_t stdout_path_len = ep ? (ep - stdout_path) : strlen(stdout_path);
388 if (stdout_path_len >= sizeof(buf))
389 return;
390
391 strncpy(buf, stdout_path, stdout_path_len);
392 buf[stdout_path_len] = '\0';
393 fdt_setprop(fdt, chosen_off, "stdout-path",
394 buf, stdout_path_len + 1);
395 }
396