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