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