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