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