1 /* $NetBSD: fdt_machdep.c,v 1.26 2018/07/17 18:41:01 christos 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.26 2018/07/17 18:41:01 christos Exp $"); 31 32 #include "opt_machdep.h" 33 #include "opt_bootconfig.h" 34 #include "opt_ddb.h" 35 #include "opt_md.h" 36 #include "opt_arm_debug.h" 37 #include "opt_multiprocessor.h" 38 #include "opt_cpuoptions.h" 39 40 #include "ukbd.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/bus.h> 45 #include <sys/atomic.h> 46 #include <sys/cpu.h> 47 #include <sys/device.h> 48 #include <sys/exec.h> 49 #include <sys/kernel.h> 50 #include <sys/kmem.h> 51 #include <sys/ksyms.h> 52 #include <sys/msgbuf.h> 53 #include <sys/proc.h> 54 #include <sys/reboot.h> 55 #include <sys/termios.h> 56 #include <sys/extent.h> 57 58 #include <dev/cons.h> 59 #include <uvm/uvm_extern.h> 60 61 #include <sys/conf.h> 62 63 #include <machine/db_machdep.h> 64 #include <ddb/db_sym.h> 65 #include <ddb/db_extern.h> 66 67 #include <machine/bootconfig.h> 68 #include <arm/armreg.h> 69 70 #include <arm/cpufunc.h> 71 #ifdef __aarch64__ 72 #include <aarch64/machdep.h> 73 #else 74 #include <arm/arm32/machdep.h> 75 #endif 76 77 78 #include <evbarm/include/autoconf.h> 79 #include <evbarm/fdt/platform.h> 80 81 #include <arm/fdt/arm_fdtvar.h> 82 83 #if NUKBD > 0 84 #include <dev/usb/ukbdvar.h> 85 #endif 86 87 #ifdef MEMORY_DISK_DYNAMIC 88 #include <dev/md.h> 89 #endif 90 91 #ifndef FDT_MAX_BOOT_STRING 92 #define FDT_MAX_BOOT_STRING 1024 93 #endif 94 95 BootConfig bootconfig; 96 char bootargs[FDT_MAX_BOOT_STRING] = ""; 97 char *boot_args = NULL; 98 99 /* filled in before cleaning bss. keep in .data */ 100 u_int uboot_args[4] __attribute__((__section__(".data"))); 101 102 const uint8_t *fdt_addr_r = (const uint8_t *)0xdeadc0de; 103 104 static char fdt_memory_ext_storage[EXTENT_FIXED_STORAGE_SIZE(DRAM_BLOCKS)]; 105 static struct extent *fdt_memory_ext; 106 107 static uint64_t initrd_start, initrd_end; 108 109 #include <libfdt.h> 110 #include <dev/fdt/fdtvar.h> 111 #define FDT_BUF_SIZE (256*1024) 112 static uint8_t fdt_data[FDT_BUF_SIZE]; 113 114 extern char KERNEL_BASE_phys[]; 115 #define KERNEL_BASE_PHYS ((paddr_t)KERNEL_BASE_phys) 116 117 static void fdt_update_stdout_path(void); 118 static void fdt_device_register(device_t, void *); 119 static void fdt_reset(void); 120 static void fdt_powerdown(void); 121 122 static dev_type_cnputc(earlyconsputc); 123 static dev_type_cngetc(earlyconsgetc); 124 125 static struct consdev earlycons = { 126 .cn_putc = earlyconsputc, 127 .cn_getc = earlyconsgetc, 128 .cn_pollc = nullcnpollc, 129 }; 130 131 static void 132 fdt_putchar(char c) 133 { 134 const struct arm_platform *plat = arm_fdt_platform(); 135 if (plat && plat->early_putchar) { 136 plat->early_putchar(c); 137 } 138 #ifdef EARLYCONS 139 else { 140 #define PLATFORM_EARLY_PUTCHAR ___CONCAT(EARLYCONS, _platform_early_putchar) 141 void PLATFORM_EARLY_PUTCHAR(char); 142 PLATFORM_EARLY_PUTCHAR(c); 143 } 144 #endif 145 } 146 147 static void 148 earlyconsputc(dev_t dev, int c) 149 { 150 fdt_putchar(c); 151 } 152 153 static int 154 earlyconsgetc(dev_t dev) 155 { 156 return 0; /* XXX */ 157 } 158 159 #ifdef VERBOSE_INIT_ARM 160 #define DPRINTF(...) printf(__VA_ARGS__) 161 #else 162 #define DPRINTF(...) 163 #endif 164 165 /* 166 * ARM: Get the first physically contiguous region of memory. 167 * ARM64: Get all of physical memory, including holes. 168 */ 169 static void 170 fdt_get_memory(uint64_t *pstart, uint64_t *pend) 171 { 172 const int memory = OF_finddevice("/memory"); 173 uint64_t cur_addr, cur_size; 174 int index; 175 176 /* Assume the first entry is the start of memory */ 177 if (fdtbus_get_reg64(memory, 0, &cur_addr, &cur_size) != 0) 178 panic("Cannot determine memory size"); 179 180 *pstart = cur_addr; 181 *pend = cur_addr + cur_size; 182 183 DPRINTF("FDT /memory [%d] @ 0x%" PRIx64 " size 0x%" PRIx64 "\n", 184 0, *pstart, *pend - *pstart); 185 186 for (index = 1; 187 fdtbus_get_reg64(memory, index, &cur_addr, &cur_size) == 0; 188 index++) { 189 DPRINTF("FDT /memory [%d] @ 0x%" PRIx64 " size 0x%" PRIx64 "\n", 190 index, cur_addr, cur_size); 191 192 #ifdef __aarch64__ 193 if (cur_addr + cur_size > *pend) 194 *pend = cur_addr + cur_size; 195 #else 196 /* If subsequent entries follow the previous, append them. */ 197 if (*pend == cur_addr) 198 *pend = cur_addr + cur_size; 199 #endif 200 } 201 } 202 203 void 204 fdt_add_reserved_memory_range(uint64_t addr, uint64_t size) 205 { 206 uint64_t start = trunc_page(addr); 207 uint64_t end = round_page(addr + size); 208 209 int error = extent_free(fdt_memory_ext, start, 210 end - start, EX_NOWAIT); 211 if (error != 0) 212 printf("MEM ERROR: res %" PRIx64 "-%" PRIx64 " failed: %d\n", 213 start, end, error); 214 else 215 DPRINTF("MEM: res %" PRIx64 "-%" PRIx64 "\n", start, end); 216 } 217 218 /* 219 * Exclude memory ranges from memory config from the device tree 220 */ 221 static void 222 fdt_add_reserved_memory(uint64_t max_addr) 223 { 224 uint64_t addr, size; 225 int index, error; 226 227 const int num = fdt_num_mem_rsv(fdtbus_get_data()); 228 for (index = 0; index <= num; index++) { 229 error = fdt_get_mem_rsv(fdtbus_get_data(), index, 230 &addr, &size); 231 if (error != 0 || size == 0) 232 continue; 233 if (addr >= max_addr) 234 continue; 235 if (addr + size > max_addr) 236 size = max_addr - addr; 237 fdt_add_reserved_memory_range(addr, size); 238 } 239 } 240 241 /* 242 * Define usable memory regions. 243 */ 244 static void 245 fdt_build_bootconfig(uint64_t mem_start, uint64_t mem_end) 246 { 247 const int memory = OF_finddevice("/memory"); 248 BootConfig *bc = &bootconfig; 249 struct extent_region *er; 250 uint64_t addr, size; 251 int index, error; 252 253 fdt_memory_ext = extent_create("FDT Memory", mem_start, mem_end, 254 fdt_memory_ext_storage, sizeof(fdt_memory_ext_storage), EX_EARLY); 255 256 for (index = 0; 257 fdtbus_get_reg64(memory, index, &addr, &size) == 0; 258 index++) { 259 if (addr >= mem_end || size == 0) 260 continue; 261 if (addr + size > mem_end) 262 size = mem_end - addr; 263 264 error = extent_alloc_region(fdt_memory_ext, addr, size, 265 EX_NOWAIT); 266 if (error != 0) 267 printf("MEM ERROR: add %" PRIx64 "-%" PRIx64 " failed: %d\n", 268 addr, addr + size, error); 269 DPRINTF("MEM: add %" PRIx64 "-%" PRIx64 "\n", addr, addr + size); 270 } 271 272 fdt_add_reserved_memory(mem_end); 273 274 const uint64_t initrd_size = initrd_end - initrd_start; 275 if (initrd_size > 0) 276 fdt_add_reserved_memory_range(initrd_start, initrd_size); 277 278 DPRINTF("Usable memory:\n"); 279 bc->dramblocks = 0; 280 LIST_FOREACH(er, &fdt_memory_ext->ex_regions, er_link) { 281 DPRINTF(" %lx - %lx\n", er->er_start, er->er_end); 282 bc->dram[bc->dramblocks].address = er->er_start; 283 bc->dram[bc->dramblocks].pages = 284 (er->er_end - er->er_start) / PAGE_SIZE; 285 bc->dramblocks++; 286 } 287 } 288 289 static void 290 fdt_probe_initrd(uint64_t *pstart, uint64_t *pend) 291 { 292 *pstart = *pend = 0; 293 294 #ifdef MEMORY_DISK_DYNAMIC 295 const int chosen = OF_finddevice("/chosen"); 296 if (chosen < 0) 297 return; 298 299 int len; 300 const void *start_data = fdtbus_get_prop(chosen, 301 "linux,initrd-start", &len); 302 const void *end_data = fdtbus_get_prop(chosen, 303 "linux,initrd-end", NULL); 304 if (start_data == NULL || end_data == NULL) 305 return; 306 307 switch (len) { 308 case 4: 309 *pstart = be32dec(start_data); 310 *pend = be32dec(end_data); 311 break; 312 case 8: 313 *pstart = be64dec(start_data); 314 *pend = be64dec(end_data); 315 break; 316 default: 317 printf("Unsupported len %d for /chosen/initrd-start\n", len); 318 return; 319 } 320 #endif 321 } 322 323 static void 324 fdt_setup_initrd(void) 325 { 326 #ifdef MEMORY_DISK_DYNAMIC 327 const uint64_t initrd_size = initrd_end - initrd_start; 328 paddr_t startpa = trunc_page(initrd_start); 329 paddr_t endpa = round_page(initrd_end); 330 paddr_t pa; 331 vaddr_t va; 332 void *md_start; 333 334 if (initrd_size == 0) 335 return; 336 337 va = uvm_km_alloc(kernel_map, initrd_size, 0, 338 UVM_KMF_VAONLY | UVM_KMF_NOWAIT); 339 if (va == 0) { 340 printf("Failed to allocate VA for initrd\n"); 341 return; 342 } 343 344 md_start = (void *)va; 345 346 for (pa = startpa; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE) 347 pmap_kenter_pa(va, pa, VM_PROT_READ|VM_PROT_WRITE, 0); 348 pmap_update(pmap_kernel()); 349 350 md_root_setconf(md_start, initrd_size); 351 #endif 352 } 353 354 u_int initarm(void *arg); 355 356 u_int 357 initarm(void *arg) 358 { 359 const struct arm_platform *plat; 360 uint64_t memory_start, memory_end; 361 362 /* set temporally to work printf()/panic() even before consinit() */ 363 cn_tab = &earlycons; 364 365 /* Load FDT */ 366 int error = fdt_check_header(fdt_addr_r); 367 if (error == 0) { 368 error = fdt_move(fdt_addr_r, fdt_data, sizeof(fdt_data)); 369 if (error != 0) 370 panic("fdt_move failed: %s", fdt_strerror(error)); 371 fdtbus_set_data(fdt_data); 372 } else { 373 panic("fdt_check_header failed: %s", fdt_strerror(error)); 374 } 375 376 /* Lookup platform specific backend */ 377 plat = arm_fdt_platform(); 378 if (plat == NULL) 379 panic("Kernel does not support this device"); 380 381 /* Early console may be available, announce ourselves. */ 382 DPRINTF("FDT<%p>\n", fdt_addr_r); 383 384 const int chosen = OF_finddevice("/chosen"); 385 if (chosen >= 0) 386 OF_getprop(chosen, "bootargs", bootargs, sizeof(bootargs)); 387 boot_args = bootargs; 388 389 DPRINTF("devmap\n"); 390 pmap_devmap_register(plat->devmap()); 391 #ifdef __aarch64__ 392 pmap_devmap_bootstrap(plat->devmap()); 393 #endif 394 395 /* Heads up ... Setup the CPU / MMU / TLB functions. */ 396 DPRINTF("cpufunc\n"); 397 if (set_cpufuncs()) 398 panic("cpu not recognized!"); 399 400 DPRINTF("bootstrap\n"); 401 plat->bootstrap(); 402 403 /* 404 * If stdout-path is specified on the command line, override the 405 * value in /chosen/stdout-path before initializing console. 406 */ 407 fdt_update_stdout_path(); 408 409 DPRINTF("consinit "); 410 consinit(); 411 DPRINTF("ok\n"); 412 413 DPRINTF("uboot: args %#lx, %#lx, %#lx, %#lx\n", 414 uboot_args[0], uboot_args[1], uboot_args[2], uboot_args[3]); 415 416 cpu_reset_address = fdt_reset; 417 cpu_powerdown_address = fdt_powerdown; 418 evbarm_device_register = fdt_device_register; 419 420 /* Talk to the user */ 421 DPRINTF("\nNetBSD/evbarm (fdt) booting ...\n"); 422 423 #ifdef BOOT_ARGS 424 char mi_bootargs[] = BOOT_ARGS; 425 parse_mi_bootargs(mi_bootargs); 426 #endif 427 428 #ifndef __aarch64__ 429 DPRINTF("KERNEL_BASE=0x%x, " 430 "KERNEL_VM_BASE=0x%x, " 431 "KERNEL_VM_BASE - KERNEL_BASE=0x%x, " 432 "KERNEL_BASE_VOFFSET=0x%x\n", 433 KERNEL_BASE, 434 KERNEL_VM_BASE, 435 KERNEL_VM_BASE - KERNEL_BASE, 436 KERNEL_BASE_VOFFSET); 437 #endif 438 439 fdt_get_memory(&memory_start, &memory_end); 440 441 #if !defined(_LP64) 442 /* Cannot map memory above 4GB */ 443 if (memory_end >= 0x100000000ULL) 444 memory_end = 0x100000000ULL - PAGE_SIZE; 445 446 uint64_t memory_size = memory_end - memory_start; 447 #endif 448 449 #ifndef __aarch64__ 450 #ifdef __HAVE_MM_MD_DIRECT_MAPPED_PHYS 451 const bool mapallmem_p = true; 452 #ifndef PMAP_NEED_ALLOC_POOLPAGE 453 if (memory_size > KERNEL_VM_BASE - KERNEL_BASE) { 454 DPRINTF("%s: dropping RAM size from %luMB to %uMB\n", 455 __func__, (unsigned long) (memory_size >> 20), 456 (KERNEL_VM_BASE - KERNEL_BASE) >> 20); 457 memory_size = KERNEL_VM_BASE - KERNEL_BASE; 458 } 459 #endif 460 #else 461 const bool mapallmem_p = false; 462 #endif 463 #endif 464 465 /* Parse ramdisk info */ 466 fdt_probe_initrd(&initrd_start, &initrd_end); 467 468 /* 469 * Populate bootconfig structure for the benefit of 470 * dodumpsys 471 */ 472 fdt_build_bootconfig(memory_start, memory_end); 473 474 #ifdef __aarch64__ 475 extern char __kernel_text[]; 476 extern char _end[]; 477 478 vaddr_t kernstart = trunc_page((vaddr_t)__kernel_text); 479 vaddr_t kernend = round_page((vaddr_t)_end); 480 481 paddr_t kernstart_phys = KERN_VTOPHYS(kernstart); 482 paddr_t kernend_phys = KERN_VTOPHYS(kernend); 483 484 DPRINTF("%s: kernel phys start %lx end %lx\n", __func__, kernstart_phys, kernend_phys); 485 486 fdt_add_reserved_memory_range(kernstart_phys, 487 kernend_phys - kernstart_phys); 488 #else 489 arm32_bootmem_init(memory_start, memory_size, KERNEL_BASE_PHYS); 490 arm32_kernel_vm_init(KERNEL_VM_BASE, ARM_VECTORS_HIGH, 0, 491 plat->devmap(), mapallmem_p); 492 #endif 493 494 DPRINTF("bootargs: %s\n", bootargs); 495 496 parse_mi_bootargs(boot_args); 497 498 #define MAX_PHYSMEM 16 499 static struct boot_physmem fdt_physmem[MAX_PHYSMEM]; 500 int nfdt_physmem = 0; 501 struct extent_region *er; 502 503 LIST_FOREACH(er, &fdt_memory_ext->ex_regions, er_link) { 504 DPRINTF(" %lx - %lx\n", er->er_start, er->er_end); 505 struct boot_physmem *bp = &fdt_physmem[nfdt_physmem++]; 506 507 KASSERT(nfdt_physmem <= MAX_PHYSMEM); 508 bp->bp_start = atop(er->er_start); 509 bp->bp_pages = atop(er->er_end - er->er_start); 510 bp->bp_freelist = VM_FREELIST_DEFAULT; 511 512 #ifdef _LP64 513 if (er->er_end > 0x100000000) 514 bp->bp_freelist = VM_FREELIST_HIGHMEM; 515 #endif 516 517 #ifdef PMAP_NEED_ALLOC_POOLPAGE 518 if (atop(memory_size) > bp->bp_pages) { 519 arm_poolpage_vmfreelist = VM_FREELIST_DIRECTMAP; 520 bp->bp_freelist = VM_FREELIST_DIRECTMAP; 521 } 522 #endif 523 } 524 525 return initarm_common(KERNEL_VM_BASE, KERNEL_VM_SIZE, fdt_physmem, 526 nfdt_physmem); 527 } 528 529 static void 530 fdt_update_stdout_path(void) 531 { 532 char *stdout_path, *ep; 533 int stdout_path_len; 534 char buf[256]; 535 536 const int chosen_off = fdt_path_offset(fdt_data, "/chosen"); 537 if (chosen_off == -1) 538 return; 539 540 if (get_bootconf_option(boot_args, "stdout-path", 541 BOOTOPT_TYPE_STRING, &stdout_path) == 0) 542 return; 543 544 ep = strchr(stdout_path, ' '); 545 stdout_path_len = ep ? (ep - stdout_path) : strlen(stdout_path); 546 if (stdout_path_len >= sizeof(buf)) 547 return; 548 549 strncpy(buf, stdout_path, stdout_path_len); 550 buf[stdout_path_len] = '\0'; 551 fdt_setprop(fdt_data, chosen_off, "stdout-path", 552 buf, stdout_path_len + 1); 553 } 554 555 void 556 consinit(void) 557 { 558 static bool initialized = false; 559 const struct arm_platform *plat = arm_fdt_platform(); 560 const struct fdt_console *cons = fdtbus_get_console(); 561 struct fdt_attach_args faa; 562 u_int uart_freq = 0; 563 564 if (initialized || cons == NULL) 565 return; 566 567 plat->init_attach_args(&faa); 568 faa.faa_phandle = fdtbus_get_stdout_phandle(); 569 570 if (plat->uart_freq != NULL) 571 uart_freq = plat->uart_freq(); 572 573 cons->consinit(&faa, uart_freq); 574 575 #if NUKBD > 0 576 ukbd_cnattach(); /* allow USB keyboard to become console */ 577 #endif 578 579 initialized = true; 580 } 581 582 void 583 delay(u_int us) 584 { 585 const struct arm_platform *plat = arm_fdt_platform(); 586 587 plat->delay(us); 588 } 589 590 static void 591 fdt_device_register(device_t self, void *aux) 592 { 593 const struct arm_platform *plat = arm_fdt_platform(); 594 595 if (device_is_a(self, "armfdt")) 596 fdt_setup_initrd(); 597 598 if (plat && plat->device_register) 599 plat->device_register(self, aux); 600 } 601 602 static void 603 fdt_reset(void) 604 { 605 const struct arm_platform *plat = arm_fdt_platform(); 606 607 fdtbus_power_reset(); 608 609 if (plat && plat->reset) 610 plat->reset(); 611 } 612 613 static void 614 fdt_powerdown(void) 615 { 616 fdtbus_power_poweroff(); 617 } 618