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