1 /* $NetBSD: fdt_boot.c,v 1.6 2024/01/21 15:10:07 kre Exp $ */ 2 3 /*- 4 * Copyright (c) 2015-2017 Jared McNeill <jmcneill@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.6 2024/01/21 15:10:07 kre Exp $"); 60 61 #include "opt_efi.h" 62 #include "opt_md.h" 63 64 #include <sys/param.h> 65 66 #include <sys/bootblock.h> 67 #include <sys/disk.h> 68 #include <sys/disklabel.h> 69 #include <sys/fcntl.h> 70 #include <sys/md5.h> 71 #include <sys/optstr.h> 72 #include <sys/rnd.h> 73 #include <sys/rndsource.h> 74 #include <sys/uuid.h> 75 #include <sys/vnode.h> 76 77 #include <net/if.h> 78 #include <net/if_dl.h> 79 80 #include <uvm/uvm_extern.h> 81 82 #include <libfdt.h> 83 84 #include <dev/fdt/fdtvar.h> 85 #include <dev/fdt/fdt_boot.h> 86 #include <dev/fdt/fdt_memory.h> 87 88 #ifndef FDT_MAX_BOOT_STRING 89 #define FDT_MAX_BOOT_STRING 1024 90 #endif 91 static char bootargs[FDT_MAX_BOOT_STRING] = ""; 92 93 #ifdef EFI_RUNTIME 94 #include <machine/efirt.h> 95 96 void fdt_map_efi_runtime(const char *, enum cpu_efirt_mem_type); 97 98 #endif 99 100 #ifdef MEMORY_DISK_DYNAMIC 101 #include <dev/md.h> 102 103 static uint64_t initrd_start, initrd_end; 104 #endif 105 106 static uint64_t rndseed_start, rndseed_end; /* our on-disk seed */ 107 static uint64_t efirng_start, efirng_end; /* firmware's EFI RNG output */ 108 static struct krndsource efirng_source; 109 110 111 static void 112 fdt_probe_range(const char *startname, const char *endname, 113 uint64_t *pstart, uint64_t *pend) 114 { 115 int chosen, len; 116 const void *start_data, *end_data; 117 118 *pstart = *pend = 0; 119 120 chosen = OF_finddevice("/chosen"); 121 if (chosen < 0) 122 return; 123 124 start_data = fdtbus_get_prop(chosen, startname, &len); 125 end_data = fdtbus_get_prop(chosen, endname, NULL); 126 if (start_data == NULL || end_data == NULL) 127 return; 128 129 switch (len) { 130 case 4: 131 *pstart = be32dec(start_data); 132 *pend = be32dec(end_data); 133 break; 134 case 8: 135 *pstart = be64dec(start_data); 136 *pend = be64dec(end_data); 137 break; 138 default: 139 printf("Unsupported len %d for /chosen `%s'\n", 140 len, startname); 141 return; 142 } 143 } 144 145 146 static void * 147 fdt_map_range(uint64_t start, uint64_t end, uint64_t *psize, 148 const char *purpose) 149 { 150 const paddr_t startpa = trunc_page(start); 151 const paddr_t endpa = round_page(end); 152 paddr_t pa; 153 vaddr_t va; 154 void *ptr; 155 156 *psize = end - start; 157 if (*psize == 0) 158 return NULL; 159 160 const vaddr_t voff = start & PAGE_MASK; 161 162 // XXX NH add an align so map_chunk works betterer? 163 va = uvm_km_alloc(kernel_map, *psize, 0, UVM_KMF_VAONLY | UVM_KMF_NOWAIT); 164 if (va == 0) { 165 printf("Failed to allocate VA for %s\n", purpose); 166 return NULL; 167 } 168 ptr = (void *)(va + voff); 169 170 // XXX NH map chunk 171 for (pa = startpa; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE) 172 pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0); 173 pmap_update(pmap_kernel()); 174 175 return ptr; 176 } 177 178 static void 179 fdt_unmap_range(void *ptr, uint64_t size) 180 { 181 const char *start = ptr, *end = start + size; 182 const vaddr_t startva = trunc_page((vaddr_t)(uintptr_t)start); 183 const vaddr_t endva = round_page((vaddr_t)(uintptr_t)end); 184 const vsize_t sz = endva - startva; 185 186 pmap_kremove(startva, sz); 187 pmap_update(pmap_kernel()); 188 189 uvm_km_free(kernel_map, startva, sz, UVM_KMF_VAONLY); 190 } 191 192 char * 193 fdt_get_bootargs(void) 194 { 195 const int chosen = OF_finddevice("/chosen"); 196 197 if (chosen >= 0) 198 OF_getprop(chosen, "bootargs", bootargs, sizeof(bootargs)); 199 return bootargs; 200 } 201 202 void 203 fdt_probe_initrd(void) 204 { 205 206 #ifdef MEMORY_DISK_DYNAMIC 207 fdt_probe_range("linux,initrd-start", "linux,initrd-end", 208 &initrd_start, &initrd_end); 209 #endif 210 } 211 212 void 213 fdt_setup_initrd(void) 214 { 215 #ifdef MEMORY_DISK_DYNAMIC 216 void *md_start; 217 uint64_t initrd_size; 218 219 md_start = fdt_map_range(initrd_start, initrd_end, &initrd_size, 220 "initrd"); 221 if (md_start == NULL) 222 return; 223 md_root_setconf(md_start, initrd_size); 224 #endif 225 } 226 227 void 228 fdt_reserve_initrd(void) 229 { 230 #ifdef MEMORY_DISK_DYNAMIC 231 const uint64_t initrd_size = 232 round_page(initrd_end) - trunc_page(initrd_start); 233 234 if (initrd_size > 0) 235 fdt_memory_remove_range(trunc_page(initrd_start), initrd_size); 236 #endif 237 } 238 239 void 240 fdt_probe_rndseed(void) 241 { 242 243 fdt_probe_range("netbsd,rndseed-start", "netbsd,rndseed-end", 244 &rndseed_start, &rndseed_end); 245 } 246 247 void 248 fdt_setup_rndseed(void) 249 { 250 uint64_t rndseed_size; 251 void *rndseed; 252 253 rndseed = fdt_map_range(rndseed_start, rndseed_end, &rndseed_size, 254 "rndseed"); 255 if (rndseed == NULL) 256 return; 257 rnd_seed(rndseed, rndseed_size); 258 fdt_unmap_range(rndseed, rndseed_size); 259 } 260 261 void 262 fdt_reserve_rndseed(void) 263 { 264 const uint64_t rndseed_size = 265 round_page(rndseed_end) - trunc_page(rndseed_start); 266 267 if (rndseed_size > 0) 268 fdt_memory_remove_range(trunc_page(rndseed_start), 269 rndseed_size); 270 } 271 272 void 273 fdt_probe_efirng(void) 274 { 275 276 fdt_probe_range("netbsd,efirng-start", "netbsd,efirng-end", 277 &efirng_start, &efirng_end); 278 } 279 280 void 281 fdt_setup_efirng(void) 282 { 283 uint64_t efirng_size; 284 void *efirng; 285 286 efirng = fdt_map_range(efirng_start, efirng_end, &efirng_size, 287 "efirng"); 288 if (efirng == NULL) 289 return; 290 291 rnd_attach_source(&efirng_source, "efirng", RND_TYPE_RNG, 292 RND_FLAG_DEFAULT); 293 294 /* 295 * We don't really have specific information about the physical 296 * process underlying the data provided by the firmware via the 297 * EFI RNG API, so the entropy estimate here is heuristic. 298 * What efiboot provides us is up to 4096 bytes of data from 299 * the EFI RNG API, although in principle it may return short. 300 * 301 * The UEFI Specification (2.8 Errata A, February 2020[1]) says 302 * 303 * When a Deterministic Random Bit Generator (DRBG) is 304 * used on the output of a (raw) entropy source, its 305 * security level must be at least 256 bits. 306 * 307 * It's not entirely clear whether `it' refers to the DRBG or 308 * the entropy source; if it refers to the DRBG, it's not 309 * entirely clear how ANSI X9.31 3DES, one of the options for 310 * DRBG in the UEFI spec, can provide a `256-bit security 311 * level' because it has only 232 bits of inputs (three 56-bit 312 * keys and one 64-bit block). That said, even if it provides 313 * only 232 bits of entropy, that's enough to prevent all 314 * attacks and we probably get a few more bits from sampling 315 * the clock anyway. 316 * 317 * In the event we get raw samples, e.g. the bits sampled by a 318 * ring oscillator, we hope that the samples have at least half 319 * a bit of entropy per bit of data -- and efiboot tries to 320 * draw 4096 bytes to provide plenty of slop. Hence we divide 321 * the total number of bits by two and clamp at 256. There are 322 * ways this could go wrong, but on most machines it should 323 * behave reasonably. 324 * 325 * [1] https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf 326 */ 327 rnd_add_data(&efirng_source, efirng, efirng_size, 328 MIN(256, efirng_size*NBBY/2)); 329 330 explicit_memset(efirng, 0, efirng_size); 331 fdt_unmap_range(efirng, efirng_size); 332 } 333 334 void 335 fdt_reserve_efirng(void) 336 { 337 const uint64_t efirng_size = 338 round_page(efirng_end) - trunc_page(efirng_start); 339 340 if (efirng_size > 0) 341 fdt_memory_remove_range(trunc_page(efirng_start), efirng_size); 342 } 343 344 #ifdef EFI_RUNTIME 345 void 346 fdt_map_efi_runtime(const char *prop, enum cpu_efirt_mem_type type) 347 { 348 int len; 349 350 const int chosen_off = fdt_path_offset(fdtbus_get_data(), "/chosen"); 351 if (chosen_off < 0) 352 return; 353 354 const uint64_t *map = fdt_getprop(fdtbus_get_data(), chosen_off, prop, &len); 355 if (map == NULL) 356 return; 357 358 while (len >= 24) { 359 const paddr_t pa = be64toh(map[0]); 360 const vaddr_t va = be64toh(map[1]); 361 const size_t sz = be64toh(map[2]); 362 #if 0 363 VPRINTF("%s: %s %#" PRIxPADDR "-%#" PRIxVADDR " (%#" PRIxVADDR 364 "-%#" PRIxVSIZE ")\n", __func__, prop, pa, pa + sz - 1, 365 va, va + sz - 1); 366 #endif 367 cpu_efirt_map_range(va, pa, sz, type); 368 map += 3; 369 len -= 24; 370 } 371 } 372 #endif 373 374 void 375 fdt_update_stdout_path(void *fdt, const char *boot_args) 376 { 377 const char *stdout_path; 378 char buf[256]; 379 380 const int chosen_off = fdt_path_offset(fdt, "/chosen"); 381 if (chosen_off == -1) 382 return; 383 384 if (optstr_get_string(boot_args, "stdout-path", &stdout_path) == false) 385 return; 386 387 const char *ep = strchr(stdout_path, ' '); 388 size_t stdout_path_len = ep ? (ep - stdout_path) : strlen(stdout_path); 389 if (stdout_path_len >= sizeof(buf)) 390 return; 391 392 strncpy(buf, stdout_path, stdout_path_len); 393 buf[stdout_path_len] = '\0'; 394 fdt_setprop(fdt, chosen_off, "stdout-path", 395 buf, stdout_path_len + 1); 396 } 397 398 static void 399 fdt_detect_root_device(device_t dev) 400 { 401 int error, len; 402 403 const int chosen = OF_finddevice("/chosen"); 404 if (chosen < 0) 405 return; 406 407 if (of_hasprop(chosen, "netbsd,mbr") && 408 of_hasprop(chosen, "netbsd,partition")) { 409 struct mbr_sector mbr; 410 uint8_t buf[DEV_BSIZE]; 411 uint8_t hash[16]; 412 const uint8_t *rhash; 413 struct vnode *vp; 414 MD5_CTX md5ctx; 415 size_t resid; 416 u_int part; 417 418 /* 419 * The bootloader has passed in a partition index and MD5 hash 420 * of the MBR sector. Read the MBR of this device, calculate the 421 * hash, and compare it with the value passed in. 422 */ 423 rhash = fdtbus_get_prop(chosen, "netbsd,mbr", &len); 424 if (rhash == NULL || len != 16) 425 return; 426 of_getprop_uint32(chosen, "netbsd,partition", &part); 427 if (part >= MAXPARTITIONS) 428 return; 429 430 vp = opendisk(dev); 431 if (!vp) 432 return; 433 error = vn_rdwr(UIO_READ, vp, buf, sizeof(buf), 0, UIO_SYSSPACE, 434 IO_NODELOCKED, NOCRED, &resid, NULL); 435 VOP_CLOSE(vp, FREAD, NOCRED); 436 vput(vp); 437 438 if (error != 0) 439 return; 440 441 memcpy(&mbr, buf, sizeof(mbr)); 442 MD5Init(&md5ctx); 443 MD5Update(&md5ctx, (void *)&mbr, sizeof(mbr)); 444 MD5Final(hash, &md5ctx); 445 446 if (memcmp(rhash, hash, 16) == 0) { 447 booted_device = dev; 448 booted_partition = part; 449 } 450 451 return; 452 } 453 454 if (of_hasprop(chosen, "netbsd,gpt-guid")) { 455 const struct uuid *guid = 456 fdtbus_get_prop(chosen, "netbsd,gpt-guid", &len); 457 458 if (guid == NULL || len != 16) 459 return; 460 461 char guidstr[UUID_STR_LEN]; 462 uuid_snprintf(guidstr, sizeof(guidstr), guid); 463 464 device_t dv = dkwedge_find_by_wname(guidstr); 465 if (dv != NULL) 466 booted_device = dv; 467 468 return; 469 } 470 471 if (of_hasprop(chosen, "netbsd,gpt-label")) { 472 const char *label = fdtbus_get_string(chosen, "netbsd,gpt-label"); 473 if (label == NULL || *label == '\0') 474 return; 475 476 device_t dv = dkwedge_find_by_wname(label); 477 if (dv != NULL) 478 booted_device = dv; 479 480 return; 481 } 482 483 if (of_hasprop(chosen, "netbsd,booted-mac-address")) { 484 const uint8_t *macaddr = 485 fdtbus_get_prop(chosen, "netbsd,booted-mac-address", &len); 486 struct ifnet *ifp; 487 488 if (macaddr == NULL || len != 6) 489 return; 490 491 int s = pserialize_read_enter(); 492 IFNET_READER_FOREACH(ifp) { 493 if (memcmp(macaddr, CLLADDR(ifp->if_sadl), len) == 0) { 494 device_t dv = device_find_by_xname(ifp->if_xname); 495 if (dv != NULL) 496 booted_device = dv; 497 break; 498 } 499 } 500 pserialize_read_exit(s); 501 502 return; 503 } 504 } 505 506 void 507 fdt_cpu_rootconf(void) 508 { 509 device_t dev; 510 deviter_t di; 511 512 if (booted_device != NULL) 513 return; 514 515 for (dev = deviter_first(&di, 0); dev; dev = deviter_next(&di)) { 516 if (device_class(dev) != DV_DISK) 517 continue; 518 519 fdt_detect_root_device(dev); 520 521 if (booted_device != NULL) 522 break; 523 } 524 deviter_release(&di); 525 } 526