fdt_machdep.c revision 1.37 1 /* $NetBSD: fdt_machdep.c,v 1.37 2018/09/16 11:24:29 skrll 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.37 2018/09/16 11:24:29 skrll 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 #include <sys/bootblock.h>
58 #include <sys/disklabel.h>
59 #include <sys/vnode.h>
60 #include <sys/kauth.h>
61 #include <sys/fcntl.h>
62 #include <sys/md5.h>
63
64 #include <dev/cons.h>
65 #include <uvm/uvm_extern.h>
66
67 #include <sys/conf.h>
68
69 #include <machine/db_machdep.h>
70 #include <ddb/db_sym.h>
71 #include <ddb/db_extern.h>
72
73 #include <machine/bootconfig.h>
74 #include <arm/armreg.h>
75
76 #include <arm/cpufunc.h>
77
78 #include <evbarm/include/autoconf.h>
79 #include <evbarm/fdt/machdep.h>
80 #include <evbarm/fdt/platform.h>
81
82 #include <arm/fdt/arm_fdtvar.h>
83
84 #if NUKBD > 0
85 #include <dev/usb/ukbdvar.h>
86 #endif
87
88 #ifdef MEMORY_DISK_DYNAMIC
89 #include <dev/md.h>
90 #endif
91
92 #ifndef FDT_MAX_BOOT_STRING
93 #define FDT_MAX_BOOT_STRING 1024
94 #endif
95
96 BootConfig bootconfig;
97 char bootargs[FDT_MAX_BOOT_STRING] = "";
98 char *boot_args = NULL;
99
100 /* filled in before cleaning bss. keep in .data */
101 u_long uboot_args[4] __attribute__((__section__(".data")));
102 const uint8_t *fdt_addr_r __attribute__((__section__(".data")));
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_cpu_rootconf(void);
120 static void fdt_reset(void);
121 static void fdt_powerdown(void);
122
123 static dev_type_cnputc(earlyconsputc);
124 static dev_type_cngetc(earlyconsgetc);
125
126 static struct consdev earlycons = {
127 .cn_putc = earlyconsputc,
128 .cn_getc = earlyconsgetc,
129 .cn_pollc = nullcnpollc,
130 };
131
132 static void
133 fdt_putchar(char c)
134 {
135 const struct arm_platform *plat = arm_fdt_platform();
136 if (plat && plat->ap_early_putchar) {
137 plat->ap_early_putchar(c);
138 }
139 #ifdef EARLYCONS
140 else {
141 #define PLATFORM_EARLY_PUTCHAR ___CONCAT(EARLYCONS, _platform_early_putchar)
142 void PLATFORM_EARLY_PUTCHAR(char);
143 PLATFORM_EARLY_PUTCHAR(c);
144 }
145 #endif
146 }
147
148 static void
149 earlyconsputc(dev_t dev, int c)
150 {
151 fdt_putchar(c);
152 }
153
154 static int
155 earlyconsgetc(dev_t dev)
156 {
157 return 0; /* XXX */
158 }
159
160 #ifdef VERBOSE_INIT_ARM
161 #define VPRINTF(...) printf(__VA_ARGS__)
162 #else
163 #define VPRINTF(...) do { } while (/* CONSTCOND */ 0)
164 #endif
165
166 /*
167 * ARM: Get the first physically contiguous region of memory.
168 * ARM64: Get all of physical memory, including holes.
169 */
170 static void
171 fdt_get_memory(uint64_t *pstart, uint64_t *pend)
172 {
173 const int memory = OF_finddevice("/memory");
174 uint64_t cur_addr, cur_size;
175 int index;
176
177 /* Assume the first entry is the start of memory */
178 if (fdtbus_get_reg64(memory, 0, &cur_addr, &cur_size) != 0)
179 panic("Cannot determine memory size");
180
181 *pstart = cur_addr;
182 *pend = cur_addr + cur_size;
183
184 VPRINTF("FDT /memory [%d] @ 0x%" PRIx64 " size 0x%" PRIx64 "\n",
185 0, *pstart, *pend - *pstart);
186
187 for (index = 1;
188 fdtbus_get_reg64(memory, index, &cur_addr, &cur_size) == 0;
189 index++) {
190 VPRINTF("FDT /memory [%d] @ 0x%" PRIx64 " size 0x%" PRIx64 "\n",
191 index, cur_addr, cur_size);
192
193 #ifdef __aarch64__
194 if (cur_addr + cur_size > *pend)
195 *pend = cur_addr + cur_size;
196 #else
197 /* If subsequent entries follow the previous, append them. */
198 if (*pend == cur_addr)
199 *pend = cur_addr + cur_size;
200 #endif
201 }
202 }
203
204 void
205 fdt_add_reserved_memory_range(uint64_t addr, uint64_t size)
206 {
207 uint64_t start = trunc_page(addr);
208 uint64_t end = round_page(addr + size);
209
210 int error = extent_free(fdt_memory_ext, start,
211 end - start, EX_NOWAIT);
212 if (error != 0)
213 printf("MEM ERROR: res %" PRIx64 "-%" PRIx64 " failed: %d\n",
214 start, end, error);
215 else
216 VPRINTF("MEM: res %" PRIx64 "-%" PRIx64 "\n", start, end);
217 }
218
219 /*
220 * Exclude memory ranges from memory config from the device tree
221 */
222 static void
223 fdt_add_reserved_memory(uint64_t min_addr, uint64_t max_addr)
224 {
225 uint64_t addr, size;
226 int index, error;
227
228 const int num = fdt_num_mem_rsv(fdtbus_get_data());
229 for (index = 0; index <= num; index++) {
230 error = fdt_get_mem_rsv(fdtbus_get_data(), index,
231 &addr, &size);
232 if (error != 0 || size == 0)
233 continue;
234 if (addr + size <= min_addr)
235 continue;
236 if (addr >= max_addr)
237 continue;
238 if (addr < min_addr) {
239 size -= (min_addr - addr);
240 addr = min_addr;
241 }
242 if (addr + size > max_addr)
243 size = max_addr - addr;
244 fdt_add_reserved_memory_range(addr, size);
245 }
246 }
247
248 /*
249 * Define usable memory regions.
250 */
251 static void
252 fdt_build_bootconfig(uint64_t mem_start, uint64_t mem_end)
253 {
254 const int memory = OF_finddevice("/memory");
255 BootConfig *bc = &bootconfig;
256 struct extent_region *er;
257 uint64_t addr, size;
258 int index, error;
259
260 fdt_memory_ext = extent_create("FDT Memory", mem_start, mem_end,
261 fdt_memory_ext_storage, sizeof(fdt_memory_ext_storage), EX_EARLY);
262
263 for (index = 0;
264 fdtbus_get_reg64(memory, index, &addr, &size) == 0;
265 index++) {
266 if (addr >= mem_end || size == 0)
267 continue;
268 if (addr + size > mem_end)
269 size = mem_end - addr;
270
271 error = extent_alloc_region(fdt_memory_ext, addr, size,
272 EX_NOWAIT);
273 if (error != 0)
274 printf("MEM ERROR: add %" PRIx64 "-%" PRIx64 " failed: %d\n",
275 addr, addr + size, error);
276 VPRINTF("MEM: add %" PRIx64 "-%" PRIx64 "\n", addr, addr + size);
277 }
278
279 fdt_add_reserved_memory(mem_start, mem_end);
280
281 const uint64_t initrd_size = initrd_end - initrd_start;
282 if (initrd_size > 0)
283 fdt_add_reserved_memory_range(initrd_start, initrd_size);
284
285 VPRINTF("Usable memory:\n");
286 bc->dramblocks = 0;
287 LIST_FOREACH(er, &fdt_memory_ext->ex_regions, er_link) {
288 VPRINTF(" %lx - %lx\n", er->er_start, er->er_end);
289 bc->dram[bc->dramblocks].address = er->er_start;
290 bc->dram[bc->dramblocks].pages =
291 (er->er_end - er->er_start) / PAGE_SIZE;
292 bc->dramblocks++;
293 }
294 }
295
296 static void
297 fdt_probe_initrd(uint64_t *pstart, uint64_t *pend)
298 {
299 *pstart = *pend = 0;
300
301 #ifdef MEMORY_DISK_DYNAMIC
302 const int chosen = OF_finddevice("/chosen");
303 if (chosen < 0)
304 return;
305
306 int len;
307 const void *start_data = fdtbus_get_prop(chosen,
308 "linux,initrd-start", &len);
309 const void *end_data = fdtbus_get_prop(chosen,
310 "linux,initrd-end", NULL);
311 if (start_data == NULL || end_data == NULL)
312 return;
313
314 switch (len) {
315 case 4:
316 *pstart = be32dec(start_data);
317 *pend = be32dec(end_data);
318 break;
319 case 8:
320 *pstart = be64dec(start_data);
321 *pend = be64dec(end_data);
322 break;
323 default:
324 printf("Unsupported len %d for /chosen/initrd-start\n", len);
325 return;
326 }
327 #endif
328 }
329
330 static void
331 fdt_setup_initrd(void)
332 {
333 #ifdef MEMORY_DISK_DYNAMIC
334 const uint64_t initrd_size = initrd_end - initrd_start;
335 paddr_t startpa = trunc_page(initrd_start);
336 paddr_t endpa = round_page(initrd_end);
337 paddr_t pa;
338 vaddr_t va;
339 void *md_start;
340
341 if (initrd_size == 0)
342 return;
343
344 va = uvm_km_alloc(kernel_map, initrd_size, 0,
345 UVM_KMF_VAONLY | UVM_KMF_NOWAIT);
346 if (va == 0) {
347 printf("Failed to allocate VA for initrd\n");
348 return;
349 }
350
351 md_start = (void *)va;
352
353 for (pa = startpa; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE)
354 pmap_kenter_pa(va, pa, VM_PROT_READ|VM_PROT_WRITE, 0);
355 pmap_update(pmap_kernel());
356
357 md_root_setconf(md_start, initrd_size);
358 #endif
359 }
360
361 u_int initarm(void *arg);
362
363 u_int
364 initarm(void *arg)
365 {
366 const struct arm_platform *plat;
367 uint64_t memory_start, memory_end;
368
369 /* set temporally to work printf()/panic() even before consinit() */
370 cn_tab = &earlycons;
371
372 /* Load FDT */
373 int error = fdt_check_header(fdt_addr_r);
374 if (error == 0) {
375 error = fdt_move(fdt_addr_r, fdt_data, sizeof(fdt_data));
376 if (error != 0)
377 panic("fdt_move failed: %s", fdt_strerror(error));
378 fdtbus_set_data(fdt_data);
379 } else {
380 panic("fdt_check_header failed: %s", fdt_strerror(error));
381 }
382
383 /* Lookup platform specific backend */
384 plat = arm_fdt_platform();
385 if (plat == NULL)
386 panic("Kernel does not support this device");
387
388 /* Early console may be available, announce ourselves. */
389 VPRINTF("FDT<%p>\n", fdt_addr_r);
390
391 const int chosen = OF_finddevice("/chosen");
392 if (chosen >= 0)
393 OF_getprop(chosen, "bootargs", bootargs, sizeof(bootargs));
394 boot_args = bootargs;
395
396 VPRINTF("devmap\n");
397 pmap_devmap_register(plat->ap_devmap());
398 #ifdef __aarch64__
399 pmap_devmap_bootstrap(plat->ap_devmap());
400 #endif
401
402 /* Heads up ... Setup the CPU / MMU / TLB functions. */
403 VPRINTF("cpufunc\n");
404 if (set_cpufuncs())
405 panic("cpu not recognized!");
406
407 VPRINTF("bootstrap\n");
408 plat->ap_bootstrap();
409
410 /*
411 * If stdout-path is specified on the command line, override the
412 * value in /chosen/stdout-path before initializing console.
413 */
414 fdt_update_stdout_path();
415
416 VPRINTF("consinit ");
417 consinit();
418 VPRINTF("ok\n");
419
420 VPRINTF("uboot: args %#lx, %#lx, %#lx, %#lx\n",
421 uboot_args[0], uboot_args[1], uboot_args[2], uboot_args[3]);
422
423 cpu_reset_address = fdt_reset;
424 cpu_powerdown_address = fdt_powerdown;
425 evbarm_device_register = fdt_device_register;
426 evbarm_cpu_rootconf = fdt_cpu_rootconf;
427
428 /* Talk to the user */
429 VPRINTF("\nNetBSD/evbarm (fdt) booting ...\n");
430
431 #ifdef BOOT_ARGS
432 char mi_bootargs[] = BOOT_ARGS;
433 parse_mi_bootargs(mi_bootargs);
434 #endif
435
436 fdt_get_memory(&memory_start, &memory_end);
437
438 #if !defined(_LP64)
439 /* Cannot map memory above 4GB */
440 if (memory_end >= 0x100000000ULL)
441 memory_end = 0x100000000ULL - PAGE_SIZE;
442
443 #endif
444 uint64_t memory_size = memory_end - memory_start;
445
446 /* Parse ramdisk info */
447 fdt_probe_initrd(&initrd_start, &initrd_end);
448
449 /*
450 * Populate bootconfig structure for the benefit of
451 * dodumpsys
452 */
453 fdt_build_bootconfig(memory_start, memory_end);
454
455 /* Perform PT build and VM init */
456 cpu_kernel_vm_init(memory_start, memory_size);
457
458 VPRINTF("bootargs: %s\n", bootargs);
459
460 parse_mi_bootargs(boot_args);
461
462 #define MAX_PHYSMEM 64
463 static struct boot_physmem fdt_physmem[MAX_PHYSMEM];
464 int nfdt_physmem = 0;
465 struct extent_region *er;
466
467 LIST_FOREACH(er, &fdt_memory_ext->ex_regions, er_link) {
468 VPRINTF(" %lx - %lx\n", er->er_start, er->er_end);
469 struct boot_physmem *bp = &fdt_physmem[nfdt_physmem++];
470
471 KASSERT(nfdt_physmem <= MAX_PHYSMEM);
472 bp->bp_start = atop(er->er_start);
473 bp->bp_pages = atop(er->er_end - er->er_start);
474 bp->bp_freelist = VM_FREELIST_DEFAULT;
475
476 #ifdef _LP64
477 if (er->er_end > 0x100000000)
478 bp->bp_freelist = VM_FREELIST_HIGHMEM;
479 #endif
480
481 #ifdef PMAP_NEED_ALLOC_POOLPAGE
482 if (atop(memory_size) > bp->bp_pages) {
483 arm_poolpage_vmfreelist = VM_FREELIST_DIRECTMAP;
484 bp->bp_freelist = VM_FREELIST_DIRECTMAP;
485 }
486 #endif
487 }
488
489 return initarm_common(KERNEL_VM_BASE, KERNEL_VM_SIZE, fdt_physmem,
490 nfdt_physmem);
491 }
492
493 static void
494 fdt_update_stdout_path(void)
495 {
496 char *stdout_path, *ep;
497 int stdout_path_len;
498 char buf[256];
499
500 const int chosen_off = fdt_path_offset(fdt_data, "/chosen");
501 if (chosen_off == -1)
502 return;
503
504 if (get_bootconf_option(boot_args, "stdout-path",
505 BOOTOPT_TYPE_STRING, &stdout_path) == 0)
506 return;
507
508 ep = strchr(stdout_path, ' ');
509 stdout_path_len = ep ? (ep - stdout_path) : strlen(stdout_path);
510 if (stdout_path_len >= sizeof(buf))
511 return;
512
513 strncpy(buf, stdout_path, stdout_path_len);
514 buf[stdout_path_len] = '\0';
515 fdt_setprop(fdt_data, chosen_off, "stdout-path",
516 buf, stdout_path_len + 1);
517 }
518
519 void
520 consinit(void)
521 {
522 static bool initialized = false;
523 const struct arm_platform *plat = arm_fdt_platform();
524 const struct fdt_console *cons = fdtbus_get_console();
525 struct fdt_attach_args faa;
526 u_int uart_freq = 0;
527
528 if (initialized || cons == NULL)
529 return;
530
531 plat->ap_init_attach_args(&faa);
532 faa.faa_phandle = fdtbus_get_stdout_phandle();
533
534 if (plat->ap_uart_freq != NULL)
535 uart_freq = plat->ap_uart_freq();
536
537 cons->consinit(&faa, uart_freq);
538
539 #if NUKBD > 0
540 ukbd_cnattach(); /* allow USB keyboard to become console */
541 #endif
542
543 initialized = true;
544 }
545
546 void
547 delay(u_int us)
548 {
549 const struct arm_platform *plat = arm_fdt_platform();
550
551 plat->ap_delay(us);
552 }
553
554 static void
555 fdt_detect_root_device(device_t dev)
556 {
557 struct mbr_sector mbr;
558 uint8_t buf[DEV_BSIZE];
559 uint8_t hash[16];
560 const uint8_t *rhash;
561 char rootarg[32];
562 struct vnode *vp;
563 MD5_CTX md5ctx;
564 int error, len;
565 size_t resid;
566 u_int part;
567
568 const int chosen = OF_finddevice("/chosen");
569 if (chosen < 0)
570 return;
571
572 if (of_hasprop(chosen, "netbsd,mbr") &&
573 of_hasprop(chosen, "netbsd,partition")) {
574
575 /*
576 * The bootloader has passed in a partition index and MD5 hash
577 * of the MBR sector. Read the MBR of this device, calculate the
578 * hash, and compare it with the value passed in.
579 */
580 rhash = fdtbus_get_prop(chosen, "netbsd,mbr", &len);
581 if (rhash == NULL || len != 16)
582 return;
583 of_getprop_uint32(chosen, "netbsd,partition", &part);
584 if (part >= MAXPARTITIONS)
585 return;
586
587 vp = opendisk(dev);
588 if (!vp)
589 return;
590 error = vn_rdwr(UIO_READ, vp, buf, sizeof(buf), 0, UIO_SYSSPACE,
591 0, NOCRED, &resid, NULL);
592 VOP_CLOSE(vp, FREAD, NOCRED);
593 vput(vp);
594
595 if (error != 0)
596 return;
597
598 memcpy(&mbr, buf, sizeof(mbr));
599 MD5Init(&md5ctx);
600 MD5Update(&md5ctx, (void *)&mbr, sizeof(mbr));
601 MD5Final(hash, &md5ctx);
602
603 if (memcmp(rhash, hash, 16) != 0)
604 return;
605
606 snprintf(rootarg, sizeof(rootarg), " root=%s%c", device_xname(dev), part + 'a');
607 strcat(boot_args, rootarg);
608 }
609 }
610
611 static void
612 fdt_device_register(device_t self, void *aux)
613 {
614 const struct arm_platform *plat = arm_fdt_platform();
615
616 if (device_is_a(self, "armfdt"))
617 fdt_setup_initrd();
618
619 if (plat && plat->ap_device_register)
620 plat->ap_device_register(self, aux);
621 }
622
623 static void
624 fdt_cpu_rootconf(void)
625 {
626 device_t dev;
627 deviter_t di;
628 char *ptr;
629
630 for (dev = deviter_first(&di, 0); dev; dev = deviter_next(&di)) {
631 if (device_class(dev) != DV_DISK)
632 continue;
633
634 if (get_bootconf_option(boot_args, "root", BOOTOPT_TYPE_STRING, &ptr) != 0)
635 break;
636
637 if (device_is_a(dev, "ld") || device_is_a(dev, "sd") || device_is_a(dev, "wd"))
638 fdt_detect_root_device(dev);
639 }
640 deviter_release(&di);
641 }
642
643 static void
644 fdt_reset(void)
645 {
646 const struct arm_platform *plat = arm_fdt_platform();
647
648 fdtbus_power_reset();
649
650 if (plat && plat->ap_reset)
651 plat->ap_reset();
652 }
653
654 static void
655 fdt_powerdown(void)
656 {
657 fdtbus_power_poweroff();
658 }
659