xref: /haiku/src/system/boot/platform/riscv/fdt.cpp (revision 46b7da1f4f40f7157d74fc7fb26ff9ec7f2416f2)
1a182bd6eSX512 /*
2a182bd6eSX512  * Copyright 2021, Haiku, Inc.
3a182bd6eSX512  * Distributed under the terms of the MIT License.
4a182bd6eSX512  */
5a182bd6eSX512 
6a182bd6eSX512 
7a182bd6eSX512 #include "fdt.h"
8a182bd6eSX512 #include <SupportDefs.h>
9a182bd6eSX512 #include <ByteOrder.h>
10a182bd6eSX512 #include <KernelExport.h>
11a182bd6eSX512 #include <boot/stage2.h>
1271530401SX512 #include <arch/generic/debug_uart_8250.h>
1371530401SX512 #include <arch/riscv64/arch_uart_sifive.h>
14*0d9b77a3SX512 #include <arch_cpu_defs.h>
15a182bd6eSX512 
16a182bd6eSX512 extern "C" {
17a182bd6eSX512 #include <libfdt.h>
18a182bd6eSX512 }
19a182bd6eSX512 
20a182bd6eSX512 #include "mmu.h"
21*0d9b77a3SX512 #include "smp.h"
22a182bd6eSX512 #include "graphics.h"
23a182bd6eSX512 #include "virtio.h"
2471530401SX512 #include "Htif.h"
2571530401SX512 #include "Clint.h"
2671530401SX512 #include "FwCfg.h"
27a182bd6eSX512 
28a182bd6eSX512 
29a182bd6eSX512 void* gFdt = NULL;
3071530401SX512 ClintRegs *volatile gClintRegs = NULL;
3171530401SX512 
3271530401SX512 static uint64 sTimerFrequrency = 10000000;
3371530401SX512 
3471530401SX512 static addr_range sPlic = {0};
3571530401SX512 static addr_range sClint = {0};
3671530401SX512 static uart_info sUart{};
3771530401SX512 
3871530401SX512 
3971530401SX512 static bool
HasFdtString(const char * prop,int size,const char * pattern)4071530401SX512 HasFdtString(const char* prop, int size, const char* pattern)
4171530401SX512 {
4271530401SX512 	int patternLen = strlen(pattern);
4371530401SX512 	const char* propEnd = prop + size;
4471530401SX512 	while (propEnd - prop > 0) {
4571530401SX512 		int curLen = strlen(prop);
4671530401SX512 		if (curLen == patternLen && memcmp(prop, pattern, curLen + 1) == 0)
4771530401SX512 			return true;
4871530401SX512 		prop += curLen + 1;
4971530401SX512 	}
5071530401SX512 	return false;
5171530401SX512 }
5271530401SX512 
5371530401SX512 
54*0d9b77a3SX512 static bool
GetReg(const void * fdt,int node,uint32 addressCells,uint32 sizeCells,size_t idx,addr_range & range)55*0d9b77a3SX512 GetReg(const void* fdt, int node, uint32 addressCells, uint32 sizeCells, size_t idx,
56*0d9b77a3SX512 	addr_range& range)
57*0d9b77a3SX512 {
58*0d9b77a3SX512 	int propSize;
59*0d9b77a3SX512 	const uint8* prop = (const uint8*)fdt_getprop(fdt, node, "reg", &propSize);
60*0d9b77a3SX512 	if (prop == NULL)
61*0d9b77a3SX512 		return false;
62*0d9b77a3SX512 
63*0d9b77a3SX512 	size_t entrySize = 4*(addressCells + sizeCells);
64*0d9b77a3SX512 	if ((idx + 1)*entrySize > (size_t)propSize)
65*0d9b77a3SX512 		return false;
66*0d9b77a3SX512 
67*0d9b77a3SX512 	prop += idx*entrySize;
68*0d9b77a3SX512 
69*0d9b77a3SX512 	switch (addressCells) {
70*0d9b77a3SX512 		case 1: range.start = fdt32_to_cpu(*(uint32*)prop); prop += 4; break;
71*0d9b77a3SX512 		case 2: range.start = fdt64_to_cpu(*(uint64*)prop); prop += 8; break;
72*0d9b77a3SX512 		default: panic("unsupported addressCells");
73*0d9b77a3SX512 	}
74*0d9b77a3SX512 	switch (sizeCells) {
75*0d9b77a3SX512 		case 1: range.size = fdt32_to_cpu(*(uint32*)prop); prop += 4; break;
76*0d9b77a3SX512 		case 2: range.size = fdt64_to_cpu(*(uint64*)prop); prop += 8; break;
77*0d9b77a3SX512 		default: panic("unsupported sizeCells");
78*0d9b77a3SX512 	}
79*0d9b77a3SX512 	return true;
80*0d9b77a3SX512 }
81*0d9b77a3SX512 
82*0d9b77a3SX512 
8371530401SX512 static uint32
GetInterrupt(const void * fdt,int node)8471530401SX512 GetInterrupt(const void* fdt, int node)
8571530401SX512 {
8671530401SX512 	if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", NULL)) {
8771530401SX512 		return fdt32_to_cpu(*(prop + 1));
8871530401SX512 	}
8971530401SX512 	if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts", NULL)) {
9071530401SX512 		return fdt32_to_cpu(*prop);
9171530401SX512 	}
9271530401SX512 	dprintf("[!] no interrupt field\n");
9371530401SX512 	return 0;
9471530401SX512 }
95a182bd6eSX512 
96a182bd6eSX512 
97a182bd6eSX512 static void
HandleFdt(const void * fdt,int node,uint32 addressCells,uint32 sizeCells,uint32 interruptCells)9871530401SX512 HandleFdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells,
9971530401SX512 	uint32 interruptCells /* from parent node */)
100a182bd6eSX512 {
101a182bd6eSX512 	// TODO: handle different field sizes
102a182bd6eSX512 
10371530401SX512 	const char* name = fdt_get_name(fdt, node, NULL);
10471530401SX512 	if (strcmp(name, "cpus") == 0) {
10571530401SX512 		if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "timebase-frequency", NULL))
10671530401SX512 			sTimerFrequrency = fdt32_to_cpu(*prop);
10771530401SX512 	}
10871530401SX512 
109a182bd6eSX512 	const char* device_type = (const char*)fdt_getprop(fdt, node,
110a182bd6eSX512 		"device_type", NULL);
111a182bd6eSX512 	if (device_type != NULL && strcmp(device_type, "memory") == 0) {
11271530401SX512 		gMemBase = (uint8*)fdt64_to_cpu(*((uint64*)fdt_getprop(fdt, node,
113a182bd6eSX512 			"reg", NULL) + 0));
11471530401SX512 		gTotalMem = fdt64_to_cpu(*((uint64*)fdt_getprop(fdt, node,
115a182bd6eSX512 			"reg", NULL) + 1));
116a182bd6eSX512 		return;
117a182bd6eSX512 	}
11871530401SX512 	int compatibleLen;
119a182bd6eSX512 	const char* compatible = (const char*)fdt_getprop(fdt, node,
12071530401SX512 		"compatible", &compatibleLen);
121a182bd6eSX512 	if (compatible == NULL) return;
12271530401SX512 	if (HasFdtString(compatible, compatibleLen, "riscv,clint0")) {
12371530401SX512 		uint64* reg = (uint64*)fdt_getprop(fdt, node, "reg", NULL);
12471530401SX512 		sClint.start = fdt64_to_cpu(*(reg + 0));
12571530401SX512 		sClint.size  = fdt64_to_cpu(*(reg + 1));
12671530401SX512 		gClintRegs = (ClintRegs*)sClint.start;
12771530401SX512 	} else if (HasFdtString(compatible, compatibleLen, "riscv,plic0")) {
128*0d9b77a3SX512 		GetReg(fdt, node, addressCells, sizeCells, 0, sPlic);
129*0d9b77a3SX512 		int propSize;
130*0d9b77a3SX512 		if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", &propSize)) {
131*0d9b77a3SX512 			dprintf("PLIC contexts\n");
132*0d9b77a3SX512 			uint32 contextId = 0;
133*0d9b77a3SX512 			for (uint32 *it = prop; (uint8_t*)it - (uint8_t*)prop < propSize; it += 2) {
134*0d9b77a3SX512 				uint32 phandle = fdt32_to_cpu(*it);
135*0d9b77a3SX512 				uint32 interrupt = fdt32_to_cpu(*(it + 1));
136*0d9b77a3SX512 				if (interrupt == sExternInt) {
137*0d9b77a3SX512 					CpuInfo* cpuInfo = smp_find_cpu(phandle);
138*0d9b77a3SX512 					dprintf("  context %" B_PRIu32 ": %" B_PRIu32 "\n", contextId, phandle);
139*0d9b77a3SX512 					if (cpuInfo != NULL) {
140*0d9b77a3SX512 						cpuInfo->plicContext = contextId;
141*0d9b77a3SX512 						dprintf("    hartId: %" B_PRIu32 "\n", cpuInfo->hartId);
142*0d9b77a3SX512 					}
143*0d9b77a3SX512 				}
144*0d9b77a3SX512 				contextId++;
145*0d9b77a3SX512 			}
146*0d9b77a3SX512 		}
14771530401SX512 	} else if (HasFdtString(compatible, compatibleLen, "virtio,mmio")) {
14871530401SX512 		uint64* reg = (uint64*)fdt_getprop(fdt, node, "reg", NULL);
149a182bd6eSX512 		virtio_register(
15071530401SX512 			fdt64_to_cpu(*(reg + 0)), fdt64_to_cpu(*(reg + 1)),
15171530401SX512 			GetInterrupt(fdt, node));
15271530401SX512 	} else if (
15371530401SX512 		strcmp(sUart.kind, "") == 0 && (
15471530401SX512 			HasFdtString(compatible, compatibleLen, "ns16550a") ||
15571530401SX512 			HasFdtString(compatible, compatibleLen, "sifive,uart0"))
15671530401SX512 	) {
15771530401SX512 		if (HasFdtString(compatible, compatibleLen, "ns16550a"))
15871530401SX512 			strcpy(sUart.kind, UART_KIND_8250);
15971530401SX512 		else if (HasFdtString(compatible, compatibleLen, "sifive,uart0"))
16071530401SX512 			strcpy(sUart.kind, UART_KIND_SIFIVE);
16171530401SX512 
16271530401SX512 		uint64* reg = (uint64*)fdt_getprop(fdt, node, "reg", NULL);
16371530401SX512 		sUart.regs.start = fdt64_to_cpu(*(reg + 0));
16471530401SX512 		sUart.regs.size  = fdt64_to_cpu(*(reg + 1));
16571530401SX512 		sUart.irq = GetInterrupt(fdt, node);
16671530401SX512 		const void* prop = fdt_getprop(fdt, node, "clock-frequency", NULL);
16771530401SX512 		sUart.clock = (prop == NULL) ? 0 : fdt32_to_cpu(*(uint32*)prop);
16871530401SX512 	} else if (HasFdtString(compatible, compatibleLen, "qemu,fw-cfg-mmio")) {
16971530401SX512 		gFwCfgRegs = (FwCfgRegs *volatile)
17071530401SX512 			fdt64_to_cpu(*(uint64*)fdt_getprop(fdt, node, "reg", NULL));
17171530401SX512 	} else if (HasFdtString(compatible, compatibleLen, "simple-framebuffer")) {
17271530401SX512 		gFramebuf.colors = (uint32*)fdt64_to_cpu(
17371530401SX512 			*(uint64*)fdt_getprop(fdt, node, "reg", NULL));
174a182bd6eSX512 		gFramebuf.stride = fdt32_to_cpu(
17571530401SX512 			*(uint32*)fdt_getprop(fdt, node, "stride", NULL)) / 4;
176a182bd6eSX512 		gFramebuf.width = fdt32_to_cpu(
17771530401SX512 			*(uint32*)fdt_getprop(fdt, node, "width", NULL));
178a182bd6eSX512 		gFramebuf.height = fdt32_to_cpu(
17971530401SX512 			*(uint32*)fdt_getprop(fdt, node, "height", NULL));
180a182bd6eSX512 	}
181a182bd6eSX512 }
182a182bd6eSX512 
183a182bd6eSX512 
184a182bd6eSX512 void
fdt_init(void * fdt)185a182bd6eSX512 fdt_init(void* fdt)
186a182bd6eSX512 {
187a182bd6eSX512 	dprintf("FDT: %p\n", fdt);
188a182bd6eSX512 	gFdt = fdt;
189a182bd6eSX512 
190a182bd6eSX512 	int res = fdt_check_header(gFdt);
191a182bd6eSX512 	if (res != 0) {
192a182bd6eSX512 		panic("Invalid FDT: %s\n", fdt_strerror(res));
193a182bd6eSX512 	}
194a182bd6eSX512 
195a182bd6eSX512 	dprintf("FDT valid, size: %" B_PRIu32 "\n", fdt_totalsize(gFdt));
196a182bd6eSX512 
197a182bd6eSX512 	int node = -1;
19871530401SX512 	int depth = -1;
19971530401SX512 	while ((node = fdt_next_node(gFdt, node, &depth)) >= 0 && depth >= 0) {
20071530401SX512 		HandleFdt(gFdt, node, 2, 2, 1);
201a182bd6eSX512 	}
202a182bd6eSX512 }
203a182bd6eSX512 
204a182bd6eSX512 
205a182bd6eSX512 void
fdt_set_kernel_args()206a182bd6eSX512 fdt_set_kernel_args()
207a182bd6eSX512 {
208c9d6d52bSAlexander von Gluck IV 	uint32_t fdtSize = fdt_totalsize(gFdt);
209c9d6d52bSAlexander von Gluck IV 
2104b5c7fe7SAlexander von Gluck IV 	// libfdt requires 8-byte alignment
2114b5c7fe7SAlexander von Gluck IV 	gKernelArgs.arch_args.fdt = (void*)(addr_t)kernel_args_malloc(fdtSize, 8);
212c9d6d52bSAlexander von Gluck IV 
21371530401SX512 	if (gKernelArgs.arch_args.fdt != NULL)
21471530401SX512 		memcpy(gKernelArgs.arch_args.fdt, gFdt, fdt_totalsize(gFdt));
21571530401SX512 	else
216a182bd6eSX512 		panic("unable to malloc for FDT!\n");
217c9d6d52bSAlexander von Gluck IV 
21871530401SX512 	gKernelArgs.arch_args.timerFrequency = sTimerFrequrency;
21971530401SX512 
22071530401SX512 	gKernelArgs.arch_args.htif.start = (addr_t)gHtifRegs;
22171530401SX512 	gKernelArgs.arch_args.htif.size = sizeof(HtifRegs);
22271530401SX512 
22371530401SX512 	gKernelArgs.arch_args.plic  = sPlic;
22471530401SX512 	gKernelArgs.arch_args.clint = sClint;
22571530401SX512 	gKernelArgs.arch_args.uart  = sUart;
226a182bd6eSX512 }
227