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