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