xref: /haiku/src/system/boot/platform/efi/dtb.cpp (revision b9cca28d489479408c158731f3581ab417cd04d0)
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 
35 extern "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 
59 static void* sDtbTable = NULL;
60 static uint32 sDtbSize = 0;
61 
62 template <typename T> DebugUART*
get_uart(addr_t base,int64 clock)63 get_uart(addr_t base, int64 clock) {
64 	static char buffer[sizeof(T)];
65 	return new(buffer) T(base, clock);
66 }
67 
68 
69 const 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
91 static void
write_string_list(const char * prop,size_t size)92 write_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 
108 static void
dump_fdt(const void * fdt)109 dump_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 
325 bool
dtb_has_fdt_string(const char * prop,int size,const char * pattern)326 dtb_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 
340 uint32
dtb_get_address_cells(const void * fdt,int node)341 dtb_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 
358 uint32
dtb_get_size_cells(const void * fdt,int node)359 dtb_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 
376 bool
dtb_get_reg(const void * fdt,int node,size_t idx,addr_range & range)377 dtb_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 
452 static int
dtb_get_interrupt_parent(const void * fdt,int node)453 dtb_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 
470 static uint32
dtb_get_interrupt_cells(const void * fdt,int node)471 dtb_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 
485 uint32
dtb_get_interrupt(const void * fdt,int node)486 dtb_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 
514 static int64
dtb_get_clock_frequency(const void * fdt,int node)515 dtb_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 
541 static void
dtb_handle_fdt(const void * fdt,int node)542 dtb_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 
576 static void
dtb_handle_chosen_node(const void * fdt)577 dtb_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 
604 void
dtb_init()605 dtb_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 
646 void
dtb_set_kernel_args()647 dtb_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