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