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