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