1 /* $NetBSD: fdt_machdep.c,v 1.106 2023/08/04 09:06:33 mrg 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 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: fdt_machdep.c,v 1.106 2023/08/04 09:06:33 mrg Exp $"); 31 32 #include "opt_arm_debug.h" 33 #include "opt_bootconfig.h" 34 #include "opt_cpuoptions.h" 35 #include "opt_ddb.h" 36 #include "opt_efi.h" 37 #include "opt_machdep.h" 38 #include "opt_multiprocessor.h" 39 40 #include "genfb.h" 41 #include "ukbd.h" 42 #include "wsdisplay.h" 43 44 #include <sys/param.h> 45 #include <sys/types.h> 46 47 #include <sys/atomic.h> 48 #include <sys/bootblock.h> 49 #include <sys/bus.h> 50 #include <sys/conf.h> 51 #include <sys/cpu.h> 52 #include <sys/device.h> 53 #include <sys/disk.h> 54 #include <sys/disklabel.h> 55 #include <sys/endian.h> 56 #include <sys/exec.h> 57 #include <sys/fcntl.h> 58 #include <sys/kauth.h> 59 #include <sys/kernel.h> 60 #include <sys/kmem.h> 61 #include <sys/ksyms.h> 62 #include <sys/md5.h> 63 #include <sys/msgbuf.h> 64 #include <sys/proc.h> 65 #include <sys/pserialize.h> 66 #include <sys/reboot.h> 67 #include <sys/systm.h> 68 #include <sys/termios.h> 69 #include <sys/vnode.h> 70 #include <sys/uuid.h> 71 72 #include <net/if.h> 73 #include <net/if_dl.h> 74 75 #include <dev/cons.h> 76 #include <uvm/uvm_extern.h> 77 78 #include <machine/db_machdep.h> 79 #include <ddb/db_sym.h> 80 #include <ddb/db_extern.h> 81 82 #include <machine/bootconfig.h> 83 #include <arm/armreg.h> 84 85 #include <arm/cpufunc.h> 86 87 #include <evbarm/include/autoconf.h> 88 #include <evbarm/fdt/machdep.h> 89 #include <evbarm/fdt/platform.h> 90 91 #include <arm/fdt/arm_fdtvar.h> 92 93 #include <dev/fdt/fdtvar.h> 94 #include <dev/fdt/fdt_boot.h> 95 #include <dev/fdt/fdt_private.h> 96 #include <dev/fdt/fdt_memory.h> 97 98 #ifdef EFI_RUNTIME 99 #include <arm/arm/efi_runtime.h> 100 #endif 101 102 #if NWSDISPLAY > 0 && NGENFB > 0 103 #include <arm/fdt/arm_simplefb.h> 104 #endif 105 106 #if NUKBD > 0 107 #include <dev/usb/ukbdvar.h> 108 #endif 109 #if NWSDISPLAY > 0 110 #include <dev/wscons/wsdisplayvar.h> 111 #endif 112 113 BootConfig bootconfig; 114 char *boot_args = NULL; 115 116 /* filled in before cleaning bss. keep in .data */ 117 u_long uboot_args[4] __attribute__((__section__(".data"))); 118 const uint8_t *fdt_addr_r __attribute__((__section__(".data"))); 119 120 #include <libfdt.h> 121 #include <dev/fdt/fdtvar.h> 122 #define FDT_BUF_SIZE (512*1024) 123 static uint8_t fdt_data[FDT_BUF_SIZE]; 124 125 extern char KERNEL_BASE_phys[]; 126 #define KERNEL_BASE_PHYS ((paddr_t)KERNEL_BASE_phys) 127 128 static void fdt_device_register(device_t, void *); 129 static void fdt_device_register_post_config(device_t, void *); 130 static void fdt_cpu_rootconf(void); 131 static void fdt_reset(void); 132 static void fdt_powerdown(void); 133 134 #if BYTE_ORDER == BIG_ENDIAN 135 static void fdt_update_fb_format(void); 136 #endif 137 138 static void 139 earlyconsputc(dev_t dev, int c) 140 { 141 uartputc(c); 142 } 143 144 static int 145 earlyconsgetc(dev_t dev) 146 { 147 return -1; 148 } 149 150 static struct consdev earlycons = { 151 .cn_putc = earlyconsputc, 152 .cn_getc = earlyconsgetc, 153 .cn_pollc = nullcnpollc, 154 }; 155 156 #ifdef VERBOSE_INIT_ARM 157 #define VPRINTF(...) printf(__VA_ARGS__) 158 #else 159 #define VPRINTF(...) __nothing 160 #endif 161 162 static void 163 fdt_add_dram_blocks(const struct fdt_memory *m, void *arg) 164 { 165 BootConfig *bc = arg; 166 167 VPRINTF(" %" PRIx64 " - %" PRIx64 "\n", m->start, m->end - 1); 168 bc->dram[bc->dramblocks].address = m->start; 169 bc->dram[bc->dramblocks].pages = 170 (m->end - m->start) / PAGE_SIZE; 171 bc->dramblocks++; 172 } 173 174 static int nfdt_physmem = 0; 175 static struct boot_physmem fdt_physmem[FDT_MEMORY_RANGES]; 176 177 static void 178 fdt_add_boot_physmem(const struct fdt_memory *m, void *arg) 179 { 180 const paddr_t saddr = round_page(m->start); 181 const paddr_t eaddr = trunc_page(m->end); 182 183 VPRINTF(" %" PRIx64 " - %" PRIx64, m->start, m->end - 1); 184 if (saddr >= eaddr) { 185 VPRINTF(" skipped\n"); 186 return; 187 } 188 VPRINTF("\n"); 189 190 struct boot_physmem *bp = &fdt_physmem[nfdt_physmem++]; 191 192 KASSERT(nfdt_physmem <= FDT_MEMORY_RANGES); 193 194 bp->bp_start = atop(saddr); 195 bp->bp_pages = atop(eaddr) - bp->bp_start; 196 bp->bp_freelist = VM_FREELIST_DEFAULT; 197 198 #ifdef PMAP_NEED_ALLOC_POOLPAGE 199 const uint64_t memory_size = *(uint64_t *)arg; 200 if (atop(memory_size) > bp->bp_pages) { 201 arm_poolpage_vmfreelist = VM_FREELIST_DIRECTMAP; 202 bp->bp_freelist = VM_FREELIST_DIRECTMAP; 203 } 204 #endif 205 } 206 207 208 static void 209 fdt_print_memory(const struct fdt_memory *m, void *arg) 210 { 211 212 VPRINTF("FDT /memory @ 0x%" PRIx64 " size 0x%" PRIx64 "\n", 213 m->start, m->end - m->start); 214 } 215 216 217 /* 218 * Define usable memory regions. 219 */ 220 static void 221 fdt_build_bootconfig(uint64_t mem_start, uint64_t mem_end) 222 { 223 BootConfig *bc = &bootconfig; 224 225 uint64_t addr, size; 226 int index; 227 228 /* Reserve pages for ramdisk, rndseed, and firmware's RNG */ 229 fdt_reserve_initrd(); 230 fdt_reserve_rndseed(); 231 fdt_reserve_efirng(); 232 233 const int framebuffer = OF_finddevice("/chosen/framebuffer"); 234 if (framebuffer >= 0) { 235 for (index = 0; 236 fdtbus_get_reg64(framebuffer, index, &addr, &size) == 0; 237 index++) { 238 fdt_memory_remove_range(addr, size); 239 } 240 } 241 242 VPRINTF("Usable memory:\n"); 243 bc->dramblocks = 0; 244 fdt_memory_foreach(fdt_add_dram_blocks, bc); 245 } 246 247 248 vaddr_t 249 initarm(void *arg) 250 { 251 const struct fdt_platform *plat; 252 uint64_t memory_start, memory_end; 253 254 /* set temporally to work printf()/panic() even before consinit() */ 255 cn_tab = &earlycons; 256 257 /* Load FDT */ 258 int error = fdt_check_header(fdt_addr_r); 259 if (error != 0) 260 panic("fdt_check_header failed: %s", fdt_strerror(error)); 261 262 /* If the DTB is too big, try to pack it in place first. */ 263 if (fdt_totalsize(fdt_addr_r) > sizeof(fdt_data)) 264 (void)fdt_pack(__UNCONST(fdt_addr_r)); 265 266 error = fdt_open_into(fdt_addr_r, fdt_data, sizeof(fdt_data)); 267 if (error != 0) 268 panic("fdt_move failed: %s", fdt_strerror(error)); 269 270 fdtbus_init(fdt_data); 271 272 /* Lookup platform specific backend */ 273 plat = fdt_platform_find(); 274 if (plat == NULL) 275 panic("Kernel does not support this device"); 276 277 /* Early console may be available, announce ourselves. */ 278 VPRINTF("FDT<%p>\n", fdt_addr_r); 279 280 boot_args = fdt_get_bootargs(); 281 282 /* Heads up ... Setup the CPU / MMU / TLB functions. */ 283 VPRINTF("cpufunc\n"); 284 if (set_cpufuncs()) 285 panic("cpu not recognized!"); 286 287 /* 288 * Memory is still identity/flat mapped this point so using ttbr for 289 * l1pt VA is fine 290 */ 291 292 VPRINTF("devmap %p\n", plat->fp_devmap()); 293 extern char ARM_BOOTSTRAP_LxPT[]; 294 pmap_devmap_bootstrap((vaddr_t)ARM_BOOTSTRAP_LxPT, plat->fp_devmap()); 295 296 VPRINTF("bootstrap\n"); 297 plat->fp_bootstrap(); 298 299 /* 300 * If stdout-path is specified on the command line, override the 301 * value in /chosen/stdout-path before initializing console. 302 */ 303 VPRINTF("stdout\n"); 304 fdt_update_stdout_path(fdt_data, boot_args); 305 306 #if BYTE_ORDER == BIG_ENDIAN 307 /* 308 * Most boards are configured to little-endian mode initially, and 309 * switched to big-endian mode after kernel is loaded. In this case, 310 * framebuffer seems byte-swapped to CPU. Override FDT to let 311 * drivers know. 312 */ 313 VPRINTF("fb_format\n"); 314 fdt_update_fb_format(); 315 #endif 316 317 /* 318 * Done making changes to the FDT. 319 */ 320 fdt_pack(fdt_data); 321 322 VPRINTF("consinit "); 323 consinit(); 324 VPRINTF("ok\n"); 325 326 VPRINTF("uboot: args %#lx, %#lx, %#lx, %#lx\n", 327 uboot_args[0], uboot_args[1], uboot_args[2], uboot_args[3]); 328 329 cpu_reset_address = fdt_reset; 330 cpu_powerdown_address = fdt_powerdown; 331 evbarm_device_register = fdt_device_register; 332 evbarm_device_register_post_config = fdt_device_register_post_config; 333 evbarm_cpu_rootconf = fdt_cpu_rootconf; 334 335 /* Talk to the user */ 336 printf("NetBSD/evbarm (fdt) booting ...\n"); 337 338 #ifdef BOOT_ARGS 339 char mi_bootargs[] = BOOT_ARGS; 340 parse_mi_bootargs(mi_bootargs); 341 #endif 342 343 fdt_memory_get(&memory_start, &memory_end); 344 345 fdt_memory_foreach(fdt_print_memory, NULL); 346 347 #if !defined(_LP64) 348 /* Cannot map memory above 4GB (remove last page as well) */ 349 const uint64_t memory_limit = 0x100000000ULL - PAGE_SIZE; 350 if (memory_end > memory_limit) { 351 fdt_memory_remove_range(memory_limit , memory_end); 352 memory_end = memory_limit; 353 } 354 #endif 355 uint64_t memory_size = memory_end - memory_start; 356 357 VPRINTF("%s: memory start %" PRIx64 " end %" PRIx64 " (len %" 358 PRIx64 ")\n", __func__, memory_start, memory_end, memory_size); 359 360 /* Parse ramdisk info */ 361 fdt_probe_initrd(); 362 363 /* Parse our on-disk rndseed and the firmware's RNG from EFI */ 364 fdt_probe_rndseed(); 365 fdt_probe_efirng(); 366 367 fdt_memory_remove_reserved(memory_start, memory_end); 368 369 /* 370 * Populate bootconfig structure for the benefit of dodumpsys 371 */ 372 VPRINTF("%s: fdt_build_bootconfig\n", __func__); 373 fdt_build_bootconfig(memory_start, memory_end); 374 375 /* Perform PT build and VM init */ 376 cpu_kernel_vm_init(memory_start, memory_size); 377 378 VPRINTF("bootargs: %s\n", boot_args); 379 380 parse_mi_bootargs(boot_args); 381 382 VPRINTF("Memory regions:\n"); 383 384 /* Populate fdt_physmem / nfdt_physmem for initarm_common */ 385 fdt_memory_foreach(fdt_add_boot_physmem, &memory_size); 386 387 vaddr_t sp = initarm_common(KERNEL_VM_BASE, KERNEL_VM_SIZE, fdt_physmem, 388 nfdt_physmem); 389 390 /* 391 * initarm_common flushes cache if required before AP start 392 */ 393 error = 0; 394 if ((boothowto & RB_MD1) == 0) { 395 VPRINTF("mpstart\n"); 396 if (plat->fp_mpstart) 397 error = plat->fp_mpstart(); 398 } 399 400 if (error) 401 return sp; 402 403 /* 404 * Now we have APs started the pages used for stacks and L1PT can 405 * be given to uvm 406 */ 407 extern char const __start__init_memory[]; 408 extern char const __stop__init_memory[] __weak; 409 410 if (&__start__init_memory[0] != &__stop__init_memory[0]) { 411 const paddr_t spa = KERN_VTOPHYS((vaddr_t)__start__init_memory); 412 const paddr_t epa = KERN_VTOPHYS((vaddr_t)__stop__init_memory); 413 const paddr_t spg = atop(spa); 414 const paddr_t epg = atop(epa); 415 416 VPRINTF(" start %08lx end %08lx... " 417 "loading in freelist %d\n", spa, epa, VM_FREELIST_DEFAULT); 418 419 uvm_page_physload(spg, epg, spg, epg, VM_FREELIST_DEFAULT); 420 } 421 422 return sp; 423 } 424 425 void 426 consinit(void) 427 { 428 static bool initialized = false; 429 const struct fdt_platform *plat = fdt_platform_find(); 430 const struct fdt_console *cons = fdtbus_get_console(); 431 struct fdt_attach_args faa; 432 u_int uart_freq = 0; 433 434 if (initialized || cons == NULL) 435 return; 436 437 plat->fp_init_attach_args(&faa); 438 faa.faa_phandle = fdtbus_get_stdout_phandle(); 439 440 if (plat->fp_uart_freq != NULL) 441 uart_freq = plat->fp_uart_freq(); 442 443 cons->consinit(&faa, uart_freq); 444 445 initialized = true; 446 } 447 448 void 449 cpu_startup_hook(void) 450 { 451 #ifdef EFI_RUNTIME 452 fdt_map_efi_runtime("netbsd,uefi-runtime-code", ARM_EFIRT_MEM_CODE); 453 fdt_map_efi_runtime("netbsd,uefi-runtime-data", ARM_EFIRT_MEM_DATA); 454 fdt_map_efi_runtime("netbsd,uefi-runtime-mmio", ARM_EFIRT_MEM_MMIO); 455 #endif 456 457 fdtbus_intr_init(); 458 459 fdt_setup_rndseed(); 460 fdt_setup_efirng(); 461 } 462 463 void 464 delay(u_int us) 465 { 466 const struct fdt_platform *plat = fdt_platform_find(); 467 468 plat->fp_delay(us); 469 } 470 471 static void 472 fdt_detect_root_device(device_t dev) 473 { 474 int error, len; 475 476 const int chosen = OF_finddevice("/chosen"); 477 if (chosen < 0) 478 return; 479 480 if (of_hasprop(chosen, "netbsd,mbr") && 481 of_hasprop(chosen, "netbsd,partition")) { 482 struct mbr_sector mbr; 483 uint8_t buf[DEV_BSIZE]; 484 uint8_t hash[16]; 485 const uint8_t *rhash; 486 struct vnode *vp; 487 MD5_CTX md5ctx; 488 size_t resid; 489 u_int part; 490 491 /* 492 * The bootloader has passed in a partition index and MD5 hash 493 * of the MBR sector. Read the MBR of this device, calculate the 494 * hash, and compare it with the value passed in. 495 */ 496 rhash = fdtbus_get_prop(chosen, "netbsd,mbr", &len); 497 if (rhash == NULL || len != 16) 498 return; 499 of_getprop_uint32(chosen, "netbsd,partition", &part); 500 if (part >= MAXPARTITIONS) 501 return; 502 503 vp = opendisk(dev); 504 if (!vp) 505 return; 506 error = vn_rdwr(UIO_READ, vp, buf, sizeof(buf), 0, UIO_SYSSPACE, 507 IO_NODELOCKED, NOCRED, &resid, NULL); 508 VOP_CLOSE(vp, FREAD, NOCRED); 509 vput(vp); 510 511 if (error != 0) 512 return; 513 514 memcpy(&mbr, buf, sizeof(mbr)); 515 MD5Init(&md5ctx); 516 MD5Update(&md5ctx, (void *)&mbr, sizeof(mbr)); 517 MD5Final(hash, &md5ctx); 518 519 if (memcmp(rhash, hash, 16) == 0) { 520 booted_device = dev; 521 booted_partition = part; 522 } 523 524 return; 525 } 526 527 if (of_hasprop(chosen, "netbsd,gpt-guid")) { 528 const struct uuid *guid = 529 fdtbus_get_prop(chosen, "netbsd,gpt-guid", &len); 530 531 if (guid == NULL || len != 16) 532 return; 533 534 char guidstr[UUID_STR_LEN]; 535 uuid_snprintf(guidstr, sizeof(guidstr), guid); 536 537 device_t dv = dkwedge_find_by_wname(guidstr); 538 if (dv != NULL) 539 booted_device = dv; 540 541 return; 542 } 543 544 if (of_hasprop(chosen, "netbsd,gpt-label")) { 545 const char *label = fdtbus_get_string(chosen, "netbsd,gpt-label"); 546 if (label == NULL || *label == '\0') 547 return; 548 549 device_t dv = dkwedge_find_by_wname(label); 550 if (dv != NULL) 551 booted_device = dv; 552 553 return; 554 } 555 556 if (of_hasprop(chosen, "netbsd,booted-mac-address")) { 557 const uint8_t *macaddr = 558 fdtbus_get_prop(chosen, "netbsd,booted-mac-address", &len); 559 struct ifnet *ifp; 560 561 if (macaddr == NULL || len != 6) 562 return; 563 564 int s = pserialize_read_enter(); 565 IFNET_READER_FOREACH(ifp) { 566 if (memcmp(macaddr, CLLADDR(ifp->if_sadl), len) == 0) { 567 device_t dv = device_find_by_xname(ifp->if_xname); 568 if (dv != NULL) 569 booted_device = dv; 570 break; 571 } 572 } 573 pserialize_read_exit(s); 574 575 return; 576 } 577 } 578 579 static void 580 fdt_device_register(device_t self, void *aux) 581 { 582 const struct fdt_platform *plat = fdt_platform_find(); 583 584 if (device_is_a(self, "armfdt")) { 585 fdt_setup_initrd(); 586 587 #if NWSDISPLAY > 0 && NGENFB > 0 588 /* 589 * Setup framebuffer console, if present. 590 */ 591 arm_simplefb_preattach(); 592 #endif 593 } 594 595 #if NWSDISPLAY > 0 && NGENFB > 0 596 if (device_is_a(self, "genfb")) { 597 prop_dictionary_t dict = device_properties(self); 598 prop_dictionary_set_uint64(dict, 599 "simplefb-physaddr", arm_simplefb_physaddr()); 600 } 601 #endif 602 603 if (plat && plat->fp_device_register) 604 plat->fp_device_register(self, aux); 605 } 606 607 static void 608 fdt_device_register_post_config(device_t self, void *aux) 609 { 610 #if NUKBD > 0 && NWSDISPLAY > 0 611 if (device_is_a(self, "wsdisplay")) { 612 struct wsdisplay_softc *sc = device_private(self); 613 if (wsdisplay_isconsole(sc)) 614 ukbd_cnattach(); 615 } 616 #endif 617 } 618 619 static void 620 fdt_cpu_rootconf(void) 621 { 622 device_t dev; 623 deviter_t di; 624 625 if (booted_device != NULL) 626 return; 627 628 for (dev = deviter_first(&di, 0); dev; dev = deviter_next(&di)) { 629 if (device_class(dev) != DV_DISK) 630 continue; 631 632 fdt_detect_root_device(dev); 633 634 if (booted_device != NULL) 635 break; 636 } 637 deviter_release(&di); 638 } 639 640 static void 641 fdt_reset(void) 642 { 643 const struct fdt_platform *plat = fdt_platform_find(); 644 645 fdtbus_power_reset(); 646 647 if (plat && plat->fp_reset) 648 plat->fp_reset(); 649 } 650 651 static void 652 fdt_powerdown(void) 653 { 654 fdtbus_power_poweroff(); 655 } 656 657 #if BYTE_ORDER == BIG_ENDIAN 658 static void 659 fdt_update_fb_format(void) 660 { 661 int off, len; 662 const char *format, *replace; 663 664 off = fdt_path_offset(fdt_data, "/chosen"); 665 if (off < 0) 666 return; 667 668 for (;;) { 669 off = fdt_node_offset_by_compatible(fdt_data, off, 670 "simple-framebuffer"); 671 if (off < 0) 672 return; 673 674 format = fdt_getprop(fdt_data, off, "format", &len); 675 if (format == NULL) 676 continue; 677 678 replace = NULL; 679 if (strcmp(format, "a8b8g8r8") == 0) 680 replace = "r8g8b8a8"; 681 else if (strcmp(format, "x8r8g8b8") == 0) 682 replace = "b8g8r8x8"; 683 if (replace != NULL) 684 fdt_setprop(fdt_data, off, "format", replace, 685 strlen(replace) + 1); 686 } 687 } 688 #endif 689