1/*
2 * Copyright 2019-2022 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *   Alexander von Gluck IV <kallisti5@unixzen.com>
7 */
8
9
10// TODO: split arch-depending code to per-arch source
11
12#include <arch_cpu_defs.h>
13#include <arch_dtb.h>
14#include <arch_smp.h>
15#include <arch/generic/debug_uart_8250.h>
16#if defined(__riscv)
17#	include <arch/riscv64/arch_uart_sifive.h>
18#elif defined(__ARM__)
19#	include <arch/arm/arch_uart_pl011.h>
20#elif defined(__aarch64__)
21#	include <arch/arm/arch_uart_pl011.h>
22#	include <arch/arm64/arch_uart_linflex.h>
23#endif
24
25
26#include <boot/addr_range.h>
27#include <boot/platform.h>
28#include <boot/stage2.h>
29#include <boot/uart.h>
30#include <string.h>
31#include <kernel/kernel.h>
32
33#include <ByteOrder.h>
34
35extern "C" {
36#include <libfdt.h>
37}
38
39#include "efi_platform.h"
40#include "serial.h"
41
42
43#define GIC_INTERRUPT_CELL_TYPE		0
44#define GIC_INTERRUPT_CELL_ID		1
45#define GIC_INTERRUPT_CELL_FLAGS	2
46#define GIC_INTERRUPT_TYPE_SPI		0
47#define GIC_INTERRUPT_TYPE_PPI		1
48#define GIC_INTERRUPT_BASE_SPI		32
49#define GIC_INTERRUPT_BASE_PPI		16
50
51
52//#define TRACE_DUMP_FDT
53
54
55#define INFO(x...) dprintf("efi/fdt: " x)
56#define ERROR(x...) dprintf("efi/fdt: " x)
57
58
59static void* sDtbTable = NULL;
60static uint32 sDtbSize = 0;
61
62template <typename T> DebugUART*
63get_uart(addr_t base, int64 clock) {
64	static char buffer[sizeof(T)];
65	return new(buffer) T(base, clock);
66}
67
68
69const struct supported_uarts {
70	const char*	dtb_compat;
71	const char*	kind;
72	DebugUART*	(*uart_driver_init)(addr_t base, int64 clock);
73} kSupportedUarts[] = {
74	{ "ns16550a", UART_KIND_8250, &get_uart<DebugUART8250> },
75	{ "ns16550", UART_KIND_8250, &get_uart<DebugUART8250> },
76	{ "snps,dw-apb-uart", UART_KIND_8250, &get_uart<DebugUART8250> },
77#if defined(__riscv)
78	{ "sifive,uart0", UART_KIND_SIFIVE, &get_uart<ArchUARTSifive> },
79#elif defined(__ARM__)
80	{ "arm,pl011", UART_KIND_PL011, &get_uart<ArchUARTPL011> },
81	{ "brcm,bcm2835-aux-uart", UART_KIND_8250, &get_uart<DebugUART8250> },
82#elif defined(__aarch64__)
83	{ "arm,pl011", UART_KIND_PL011, &get_uart<ArchUARTPL011> },
84	{ "fsl,s32-linflexuart", UART_KIND_LINFLEX, &get_uart<ArchUARTlinflex> },
85	{ "brcm,bcm2835-aux-uart", UART_KIND_8250, &get_uart<DebugUART8250> },
86#endif
87};
88
89
90#ifdef TRACE_DUMP_FDT
91static void
92write_string_list(const char* prop, size_t size)
93{
94	bool first = true;
95	const char* propEnd = prop + size;
96	while (propEnd - prop > 0) {
97		if (first)
98			first = false;
99		else
100			dprintf(", ");
101		int curLen = strlen(prop);
102		dprintf("'%s'", prop);
103		prop += curLen + 1;
104	}
105}
106
107
108static void
109dump_fdt(const void *fdt)
110{
111	if (!fdt)
112		return;
113
114	int err = fdt_check_header(fdt);
115	if (err) {
116		dprintf("fdt error: %s\n", fdt_strerror(err));
117		return;
118	}
119
120	dprintf("fdt tree:\n");
121
122	int node = -1;
123	int depth = -1;
124	while ((node = fdt_next_node(fdt, node, &depth)) >= 0 && depth >= 0) {
125		for (int i = 0; i < depth; i++)
126			dprintf("  ");
127		// WriteInt(node); WriteString(", "); WriteInt(depth); WriteString(": ");
128		dprintf("node('%s')\n", fdt_get_name(fdt, node, NULL));
129		depth++;
130		for (int prop = fdt_first_property_offset(fdt, node); prop >= 0; prop = fdt_next_property_offset(fdt, prop)) {
131			int len;
132			const struct fdt_property *property = fdt_get_property_by_offset(fdt, prop, &len);
133			if (property == NULL) {
134				for (int i = 0; i < depth; i++)
135					dprintf("  ");
136				dprintf("getting prop at %d: %s\n", prop, fdt_strerror(len));
137				break;
138			}
139			for (int i = 0; i < depth; i++)
140				dprintf("  ");
141			dprintf("prop('%s'): ", fdt_string(fdt, fdt32_to_cpu(property->nameoff)));
142			if (
143				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "compatible") == 0 ||
144				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "model") == 0 ||
145				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "serial-number") == 0 ||
146				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "status") == 0 ||
147				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "device_type") == 0 ||
148				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "riscv,isa") == 0 ||
149				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "mmu-type") == 0 ||
150				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "format") == 0 ||
151				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "bootargs") == 0 ||
152				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "stdout-path") == 0 ||
153				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "reg-names") == 0 ||
154				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "reset-names") == 0 ||
155				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "clock-names") == 0 ||
156				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "clock-output-names") == 0
157			) {
158				write_string_list((const char*)property->data, fdt32_to_cpu(property->len));
159			} else if (strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "reg") == 0) {
160				for (uint64_t *it = (uint64_t*)property->data; (uint8_t*)it - (uint8_t*)property->data < fdt32_to_cpu(property->len); it += 2) {
161					if (it != (uint64_t*)property->data)
162						dprintf(", ");
163					dprintf("(0x%08" B_PRIx64 ", 0x%08" B_PRIx64 ")",
164						fdt64_to_cpu(*it), fdt64_to_cpu(*(it + 1)));
165				}
166			} else if (
167				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "phandle") == 0 ||
168				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "clock-frequency") == 0 ||
169				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "timebase-frequency") == 0 ||
170				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "#address-cells") == 0 ||
171				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "#size-cells") == 0 ||
172				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "#interrupt-cells") == 0 ||
173				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "interrupts") == 0 ||
174				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "interrupt-parent") == 0 ||
175				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "boot-hartid") == 0 ||
176				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "riscv,ndev") == 0 ||
177				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "value") == 0 ||
178				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "offset") == 0 ||
179				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "regmap") == 0 ||
180				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "bank-width") == 0 ||
181				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "width") == 0 ||
182				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "height") == 0 ||
183				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "stride") == 0
184			) {
185				dprintf("%" B_PRId32, fdt32_to_cpu(*(uint32_t*)property->data));
186			} else if (
187				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "interrupts-extended") == 0
188			) {
189				for (uint32_t *it = (uint32_t*)property->data; (uint8_t*)it - (uint8_t*)property->data < fdt32_to_cpu(property->len); it += 2) {
190					if (it != (uint32_t*)property->data)
191						dprintf(", ");
192					dprintf("(%" B_PRId32 ", %" B_PRId32 ")",
193						fdt32_to_cpu(*it), fdt32_to_cpu(*(it + 1)));
194				}
195			} else if (
196				strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "ranges") == 0
197			) {
198				dprintf("\n");
199				depth++;
200				// kind
201				// child address
202				// parent address
203				// size
204				for (uint32_t *it = (uint32_t*)property->data; (uint8_t*)it - (uint8_t*)property->data < fdt32_to_cpu(property->len); it += 7) {
205					for (int i = 0; i < depth; i++)
206						dprintf("  ");
207					uint32_t kind = fdt32_to_cpu(*(it + 0));
208					switch (kind & 0x03000000) {
209					case 0x00000000: dprintf("CONFIG"); break;
210					case 0x01000000: dprintf("IOPORT"); break;
211					case 0x02000000: dprintf("MMIO"); break;
212					case 0x03000000: dprintf("MMIO_64BIT"); break;
213					}
214					dprintf(" (0x%08" PRIx32 "), child: 0x%08" PRIx64 ", parent: 0x%08" PRIx64 ", len: 0x%08" PRIx64 "\n",
215						kind, fdt64_to_cpu(*(uint64_t*)(it + 1)), fdt64_to_cpu(*(uint64_t*)(it + 3)), fdt64_to_cpu(*(uint64_t*)(it + 5)));
216				}
217				for (int i = 0; i < depth; i++)
218					dprintf("  ");
219				depth--;
220			} else if (strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "bus-range") == 0) {
221				uint32_t *it = (uint32_t*)property->data;
222				dprintf("%" PRId32 ", %" PRId32, fdt32_to_cpu(*it), fdt32_to_cpu(*(it + 1)));
223			} else if (strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "interrupt-map-mask") == 0) {
224				dprintf("\n");
225				depth++;
226				for (uint32_t *it = (uint32_t*)property->data; (uint8_t*)it - (uint8_t*)property->data < fdt32_to_cpu(property->len); it++) {
227					for (int i = 0; i < depth; i++)
228						dprintf("  ");
229					dprintf("0x%08" PRIx32 "\n", fdt32_to_cpu(*(uint32_t*)it));
230				}
231				for (int i = 0; i < depth; i++)
232					dprintf("  ");
233				depth--;
234			} else if (strcmp(fdt_string(fdt, fdt32_to_cpu(property->nameoff)), "interrupt-map") == 0) {
235				dprintf("\n");
236				depth++;
237
238				int addressCells = 3;
239				int interruptCells = 1;
240				int phandleCells = 1;
241
242				uint32 *prop;
243
244				prop = (uint32*)fdt_getprop(fdt, node, "#address-cells", NULL);
245				if (prop != NULL)
246					addressCells = fdt32_to_cpu(*prop);
247
248				prop = (uint32*)fdt_getprop(fdt, node, "#interrupt-cells", NULL);
249				if (prop != NULL)
250					interruptCells = fdt32_to_cpu(*prop);
251
252				uint32_t *it = (uint32_t*)property->data;
253				while ((uint8_t*)it - (uint8_t*)property->data < fdt32_to_cpu(property->len)) {
254					for (int i = 0; i < depth; i++)
255						dprintf("  ");
256
257					uint32 childAddr = fdt32_to_cpu(*it);
258					dprintf("childAddr: ");
259					for (int i = 0; i < addressCells; i++) {
260						dprintf("0x%08" PRIx32 " ", fdt32_to_cpu(*it));
261						it++;
262					}
263
264					uint8 bus = childAddr / (1 << 16) % (1 << 8);
265					uint8 dev = childAddr / (1 << 11) % (1 << 5);
266					uint8 func = childAddr % (1 << 3);
267					dprintf("(bus: %" PRId32 ", dev: %" PRId32 ", fn: %" PRId32 "), ",
268						bus, dev, func);
269
270					dprintf("childIrq: ");
271					for (int i = 0; i < interruptCells; i++) {
272						dprintf("%" PRIu32 " ", fdt32_to_cpu(*it));
273						it++;
274					}
275
276					uint32 parentPhandle = fdt32_to_cpu(*it);
277					it += phandleCells;
278					dprintf("parentPhandle: %" PRId32 ", ", parentPhandle);
279
280					int parentAddressCells = 0;
281					int parentInterruptCells = 1;
282
283					int parentNode = fdt_node_offset_by_phandle(fdt, parentPhandle);
284					if (parentNode >= 0) {
285						prop = (uint32*)fdt_getprop(fdt, parentNode, "#address-cells", NULL);
286						if (prop != NULL)
287							parentAddressCells = fdt32_to_cpu(*prop);
288
289						prop = (uint32*)fdt_getprop(fdt, parentNode, "#interrupt-cells", NULL);
290						if (prop != NULL)
291							parentInterruptCells = fdt32_to_cpu(*prop);
292					}
293
294					dprintf("parentAddress: ");
295					for (int i = 0; i < parentAddressCells; i++) {
296						dprintf("%" PRIu32 " ", fdt32_to_cpu(*it));
297						it++;
298					}
299
300					dprintf("parentIrq: ");
301					for (int i = 0; i < parentInterruptCells; i++) {
302						dprintf("%" PRIu32 " ", fdt32_to_cpu(*it));
303						it++;
304					}
305
306					dprintf("\n");
307				}
308				for (int i = 0; i < depth; i++)
309					dprintf("  ");
310				depth--;
311			} else {
312				dprintf("?");
313			}
314			dprintf(" (len %" PRId32 ")\n", fdt32_to_cpu(property->len));
315/*
316			dump_hex(property->data, fdt32_to_cpu(property->len), depth);
317*/
318		}
319		depth--;
320	}
321}
322#endif
323
324
325bool
326dtb_has_fdt_string(const char* prop, int size, const char* pattern)
327{
328	int patternLen = strlen(pattern);
329	const char* propEnd = prop + size;
330	while (propEnd - prop > 0) {
331		int curLen = strlen(prop);
332		if (curLen == patternLen && memcmp(prop, pattern, curLen + 1) == 0)
333			return true;
334		prop += curLen + 1;
335	}
336	return false;
337}
338
339
340uint32
341dtb_get_address_cells(const void* fdt, int node)
342{
343	uint32 res = 2;
344
345	int parent = fdt_parent_offset(fdt, node);
346	if (parent < 0)
347		return res;
348
349	uint32 *prop = (uint32*)fdt_getprop(fdt, parent, "#address-cells", NULL);
350	if (prop == NULL)
351		return res;
352
353	res = fdt32_to_cpu(*prop);
354	return res;
355}
356
357
358uint32
359dtb_get_size_cells(const void* fdt, int node)
360{
361	uint32 res = 1;
362
363	int parent = fdt_parent_offset(fdt, node);
364	if (parent < 0)
365		return res;
366
367	uint32 *prop = (uint32*)fdt_getprop(fdt, parent, "#size-cells", NULL);
368	if (prop == NULL)
369		return res;
370
371	res = fdt32_to_cpu(*prop);
372	return res;
373}
374
375
376bool
377dtb_get_reg(const void* fdt, int node, size_t idx, addr_range& range)
378{
379	uint32 addressCells = dtb_get_address_cells(fdt, node);
380	uint32 sizeCells = dtb_get_size_cells(fdt, node);
381
382	int propSize;
383	const uint8* prop = (const uint8*)fdt_getprop(fdt, node, "reg", &propSize);
384	if (prop == NULL)
385		return false;
386
387	size_t entrySize = 4 * (addressCells + sizeCells);
388	if ((idx + 1) * entrySize > (size_t)propSize)
389		return false;
390
391	prop += idx * entrySize;
392
393	switch (addressCells) {
394		case 1: range.start = fdt32_to_cpu(*(uint32*)prop); prop += 4; break;
395		case 2: range.start = fdt64_to_cpu(*(uint64*)prop); prop += 8; break;
396		default: panic("unsupported addressCells");
397	}
398	switch (sizeCells) {
399		case 1: range.size = fdt32_to_cpu(*(uint32*)prop); prop += 4; break;
400		case 2: range.size = fdt64_to_cpu(*(uint64*)prop); prop += 8; break;
401		default: panic("unsupported sizeCells");
402	}
403
404	int parent = fdt_parent_offset(fdt, node);
405	if (parent >= 0) {
406		uint32 parentAddressCells = dtb_get_address_cells(fdt, parent);
407
408		uint32 rangesSize = 0;
409		uint32 *ranges = (uint32 *)fdt_getprop(fdt, parent, "ranges", (int *)&rangesSize);
410		if (ranges == NULL)
411			return true;
412
413		uint32 rangesPos = 0;
414		while (rangesSize >= (rangesPos + parentAddressCells + addressCells + sizeCells)) {
415			addr_t childAddress;
416			addr_t parentAddress;
417			size_t rangeSize;
418
419			if (addressCells == 1) {
420				childAddress = fdt32_to_cpu(*(uint32*)(ranges+rangesPos));
421			} else {
422				childAddress = fdt64_to_cpu(*(uint64*)(ranges+rangesPos));
423			}
424			rangesPos += addressCells;
425
426			if (parentAddressCells == 1) {
427				parentAddress = fdt32_to_cpu(*(uint32*)(ranges+rangesPos));
428			} else {
429				parentAddress = fdt64_to_cpu(*(uint64*)(ranges+rangesPos));
430			}
431			rangesPos += parentAddressCells;
432
433			if (sizeCells == 1) {
434				rangeSize = fdt32_to_cpu(*(uint32*)(ranges+rangesPos));
435			} else {
436				rangeSize = fdt64_to_cpu(*(uint64*)(ranges+rangesPos));
437			}
438			rangesPos += sizeCells;
439
440			if ((range.start >= childAddress) && (range.start <= childAddress + rangeSize)) {
441				range.start -= childAddress;
442				range.start += parentAddress;
443				break;
444			}
445		}
446	}
447
448	return true;
449}
450
451
452static int
453dtb_get_interrupt_parent(const void* fdt, int node)
454{
455	while (node >= 0) {
456		uint32* prop;
457		prop = (uint32*)fdt_getprop(fdt, node, "interrupt-parent", NULL);
458		if (prop != NULL) {
459			uint32_t phandle = fdt32_to_cpu(*prop);
460			return fdt_node_offset_by_phandle(fdt, phandle);
461		}
462
463		node = fdt_parent_offset(fdt, node);
464	}
465
466	return -1;
467}
468
469
470static uint32
471dtb_get_interrupt_cells(const void* fdt, int node)
472{
473	int intc_node = dtb_get_interrupt_parent(fdt, node);
474	if (intc_node >= 0) {
475		uint32* prop = (uint32*)fdt_getprop(fdt, intc_node, "#interrupt-cells", NULL);
476		if (prop != NULL) {
477			return fdt32_to_cpu(*prop);
478		}
479	}
480
481	return 1;
482}
483
484
485uint32
486dtb_get_interrupt(const void* fdt, int node)
487{
488	uint32 interruptCells = dtb_get_interrupt_cells(fdt, node);
489
490	if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", NULL)) {
491		return fdt32_to_cpu(*(prop + 1));
492	}
493	if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts", NULL)) {
494		if ((interruptCells == 1) || (interruptCells == 2)) {
495			return fdt32_to_cpu(*prop);
496		} else if (interruptCells == 3) {
497			uint32 interruptType = fdt32_to_cpu(prop[GIC_INTERRUPT_CELL_TYPE]);
498			uint32 interruptNumber = fdt32_to_cpu(prop[GIC_INTERRUPT_CELL_ID]);
499			if (interruptType == GIC_INTERRUPT_TYPE_SPI)
500				interruptNumber += GIC_INTERRUPT_BASE_SPI;
501			else if (interruptType == GIC_INTERRUPT_TYPE_PPI)
502				interruptNumber += GIC_INTERRUPT_BASE_PPI;
503
504			return interruptNumber;
505		} else {
506			panic("unsupported interruptCells");
507		}
508	}
509	dprintf("[!] no interrupt field\n");
510	return 0;
511}
512
513
514static int64
515dtb_get_clock_frequency(const void* fdt, int node)
516{
517	uint32* prop;
518	int len = 0;
519
520	prop = (uint32*)fdt_getprop(fdt, node, "clock-frequency", NULL);
521	if (prop != NULL) {
522		return fdt32_to_cpu(*prop);
523	}
524
525	prop = (uint32*)fdt_getprop(fdt, node, "clocks", &len);
526	if (prop != NULL) {
527		uint32_t phandle = fdt32_to_cpu(*prop);
528		int offset = fdt_node_offset_by_phandle(fdt, phandle);
529		if (offset > 0) {
530			uint32* prop2 = (uint32*)fdt_getprop(fdt, offset, "clock-frequency", NULL);
531			if (prop2 != NULL) {
532				return fdt32_to_cpu(*prop2);
533			}
534		}
535	}
536
537	return 0;
538}
539
540
541static void
542dtb_handle_fdt(const void* fdt, int node)
543{
544	arch_handle_fdt(fdt, node);
545
546	int compatibleLen;
547	const char* compatible = (const char*)fdt_getprop(fdt, node,
548		"compatible", &compatibleLen);
549
550	if (compatible == NULL)
551		return;
552
553	// check for a uart if we don't have one
554	uart_info &uart = gKernelArgs.arch_args.uart;
555	if (uart.kind[0] == 0) {
556		for (uint32 i = 0; i < B_COUNT_OF(kSupportedUarts); i++) {
557			if (dtb_has_fdt_string(compatible, compatibleLen,
558					kSupportedUarts[i].dtb_compat)) {
559
560				memcpy(uart.kind, kSupportedUarts[i].kind,
561					sizeof(uart.kind));
562
563				dtb_get_reg(fdt, node, 0, uart.regs);
564				uart.irq = dtb_get_interrupt(fdt, node);
565				uart.clock = dtb_get_clock_frequency(fdt, node);
566
567				gUART = kSupportedUarts[i].uart_driver_init(uart.regs.start,
568					uart.clock);
569				gUARTSkipInit = fdt_getprop(fdt, node, "skip-init", NULL) != NULL;
570			}
571		}
572	}
573}
574
575
576static void
577dtb_handle_chosen_node(const void *fdt)
578{
579	int chosen = fdt_path_offset(fdt, "/chosen");
580	if (chosen < 0)
581		return;
582
583	int len;
584	const char *stdoutPath = (const char *)fdt_getprop(fdt, chosen, "stdout-path", &len);
585	if (stdoutPath == NULL)
586		return;
587
588	// stdout-path can optionally contain a ":" separator character
589	// The part after the ":" character specifies the UART configuration
590	// We can ignore it here as the UART should be already initialized
591	// by the UEFI firmware (e.g. U-Boot or TianoCore)
592
593	char *separator = strchr(stdoutPath, ':');
594	int namelen = (separator == NULL) ? len - 1 : separator - stdoutPath;
595
596	int stdoutNode = fdt_path_offset_namelen(fdt, stdoutPath, namelen);
597	if (stdoutNode < 0)
598		return;
599
600	dtb_handle_fdt(fdt, stdoutNode);
601}
602
603
604void
605dtb_init()
606{
607	efi_configuration_table *table = kSystemTable->ConfigurationTable;
608	size_t entries = kSystemTable->NumberOfTableEntries;
609
610	INFO("Probing for device trees from UEFI...\n");
611
612	// Try to find an FDT
613	for (uint32 i = 0; i < entries; i++) {
614		if (!table[i].VendorGuid.equals(DEVICE_TREE_GUID))
615			continue;
616
617		void* dtbPtr = (void*)(table[i].VendorTable);
618
619		int res = fdt_check_header(dtbPtr);
620		if (res != 0) {
621			ERROR("Invalid FDT from UEFI table %d: %s\n", i, fdt_strerror(res));
622			continue;
623		}
624
625		sDtbTable = dtbPtr;
626		sDtbSize = fdt_totalsize(dtbPtr);
627
628		INFO("Valid FDT from UEFI table %d, size: %" B_PRIu32 "\n", i, sDtbSize);
629
630#ifdef TRACE_DUMP_FDT
631		dump_fdt(sDtbTable);
632#endif
633
634		dtb_handle_chosen_node(sDtbTable);
635
636		int node = -1;
637		int depth = -1;
638		while ((node = fdt_next_node(sDtbTable, node, &depth)) >= 0 && depth >= 0) {
639			dtb_handle_fdt(sDtbTable, node);
640		}
641		break;
642	}
643}
644
645
646void
647dtb_set_kernel_args()
648{
649	// pack into proper location if the architecture cares
650	if (sDtbTable != NULL) {
651		// libfdt requires 8-byte alignment
652		gKernelArgs.arch_args.fdt = (void*)(addr_t)kernel_args_malloc(sDtbSize, 8);
653
654		if (gKernelArgs.arch_args.fdt != NULL)
655			memcpy(gKernelArgs.arch_args.fdt, sDtbTable, sDtbSize);
656		else
657			ERROR("unable to malloc for fdt!\n");
658	}
659
660	uart_info &uart = gKernelArgs.arch_args.uart;
661	dprintf("Chosen UART:\n");
662	if (uart.kind[0] == 0) {
663		dprintf("kind: None!\n");
664	} else {
665		dprintf("  kind: %s\n", uart.kind);
666		dprintf("  regs: %#" B_PRIx64 ", %#" B_PRIx64 "\n", uart.regs.start, uart.regs.size);
667		dprintf("  irq: %" B_PRIu32 "\n", uart.irq);
668		dprintf("  clock: %" B_PRIu64 "\n", uart.clock);
669	}
670
671	arch_dtb_set_kernel_args();
672}
673