xref: /netbsd-src/sys/arch/evbarm/fdt/fdt_machdep.c (revision 796c32c94f6e154afc9de0f63da35c91bb739b45)
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