xref: /haiku/src/system/boot/platform/riscv/fdt.cpp (revision a127b88ecbfab58f64944c98aa47722a18e363b2)
1 /*
2  * Copyright 2021, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "fdt.h"
8 #include <SupportDefs.h>
9 #include <ByteOrder.h>
10 #include <KernelExport.h>
11 #include <boot/stage2.h>
12 #include <arch/generic/debug_uart_8250.h>
13 #include <arch/riscv64/arch_uart_sifive.h>
14 #include <arch_cpu_defs.h>
15 
16 extern "C" {
17 #include <libfdt.h>
18 }
19 
20 #include "mmu.h"
21 #include "smp.h"
22 #include "graphics.h"
23 #include "virtio.h"
24 #include "Htif.h"
25 #include "Clint.h"
26 #include "FwCfg.h"
27 
28 
29 void* gFdt = NULL;
30 ClintRegs *volatile gClintRegs = NULL;
31 
32 static uint64 sTimerFrequrency = 10000000;
33 
34 static addr_range sPlic = {0};
35 static addr_range sClint = {0};
36 static uart_info sUart{};
37 
38 
39 static bool
40 HasFdtString(const char* prop, int size, const char* pattern)
41 {
42 	int patternLen = strlen(pattern);
43 	const char* propEnd = prop + size;
44 	while (propEnd - prop > 0) {
45 		int curLen = strlen(prop);
46 		if (curLen == patternLen && memcmp(prop, pattern, curLen + 1) == 0)
47 			return true;
48 		prop += curLen + 1;
49 	}
50 	return false;
51 }
52 
53 
54 static bool
55 GetReg(const void* fdt, int node, uint32 addressCells, uint32 sizeCells, size_t idx,
56 	addr_range& range)
57 {
58 	int propSize;
59 	const uint8* prop = (const uint8*)fdt_getprop(fdt, node, "reg", &propSize);
60 	if (prop == NULL)
61 		return false;
62 
63 	size_t entrySize = 4*(addressCells + sizeCells);
64 	if ((idx + 1)*entrySize > (size_t)propSize)
65 		return false;
66 
67 	prop += idx*entrySize;
68 
69 	switch (addressCells) {
70 		case 1: range.start = fdt32_to_cpu(*(uint32*)prop); prop += 4; break;
71 		case 2: range.start = fdt64_to_cpu(*(uint64*)prop); prop += 8; break;
72 		default: panic("unsupported addressCells");
73 	}
74 	switch (sizeCells) {
75 		case 1: range.size = fdt32_to_cpu(*(uint32*)prop); prop += 4; break;
76 		case 2: range.size = fdt64_to_cpu(*(uint64*)prop); prop += 8; break;
77 		default: panic("unsupported sizeCells");
78 	}
79 	return true;
80 }
81 
82 
83 static uint32
84 GetInterrupt(const void* fdt, int node)
85 {
86 	if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", NULL)) {
87 		return fdt32_to_cpu(*(prop + 1));
88 	}
89 	if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts", NULL)) {
90 		return fdt32_to_cpu(*prop);
91 	}
92 	dprintf("[!] no interrupt field\n");
93 	return 0;
94 }
95 
96 
97 static void
98 HandleFdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells,
99 	uint32 interruptCells /* from parent node */)
100 {
101 	// TODO: handle different field sizes
102 
103 	const char* name = fdt_get_name(fdt, node, NULL);
104 	if (strcmp(name, "cpus") == 0) {
105 		if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "timebase-frequency", NULL))
106 			sTimerFrequrency = fdt32_to_cpu(*prop);
107 	}
108 
109 	const char* device_type = (const char*)fdt_getprop(fdt, node,
110 		"device_type", NULL);
111 	if (device_type != NULL && strcmp(device_type, "memory") == 0) {
112 		gMemBase = (uint8*)fdt64_to_cpu(*((uint64*)fdt_getprop(fdt, node,
113 			"reg", NULL) + 0));
114 		gTotalMem = fdt64_to_cpu(*((uint64*)fdt_getprop(fdt, node,
115 			"reg", NULL) + 1));
116 		return;
117 	}
118 	int compatibleLen;
119 	const char* compatible = (const char*)fdt_getprop(fdt, node,
120 		"compatible", &compatibleLen);
121 	if (compatible == NULL) return;
122 	if (HasFdtString(compatible, compatibleLen, "riscv,clint0")) {
123 		uint64* reg = (uint64*)fdt_getprop(fdt, node, "reg", NULL);
124 		sClint.start = fdt64_to_cpu(*(reg + 0));
125 		sClint.size  = fdt64_to_cpu(*(reg + 1));
126 		gClintRegs = (ClintRegs*)sClint.start;
127 	} else if (HasFdtString(compatible, compatibleLen, "riscv,plic0")) {
128 		GetReg(fdt, node, addressCells, sizeCells, 0, sPlic);
129 		int propSize;
130 		if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", &propSize)) {
131 			dprintf("PLIC contexts\n");
132 			uint32 contextId = 0;
133 			for (uint32 *it = prop; (uint8_t*)it - (uint8_t*)prop < propSize; it += 2) {
134 				uint32 phandle = fdt32_to_cpu(*it);
135 				uint32 interrupt = fdt32_to_cpu(*(it + 1));
136 				if (interrupt == sExternInt) {
137 					CpuInfo* cpuInfo = smp_find_cpu(phandle);
138 					dprintf("  context %" B_PRIu32 ": %" B_PRIu32 "\n", contextId, phandle);
139 					if (cpuInfo != NULL) {
140 						cpuInfo->plicContext = contextId;
141 						dprintf("    hartId: %" B_PRIu32 "\n", cpuInfo->hartId);
142 					}
143 				}
144 				contextId++;
145 			}
146 		}
147 	} else if (HasFdtString(compatible, compatibleLen, "virtio,mmio")) {
148 		uint64* reg = (uint64*)fdt_getprop(fdt, node, "reg", NULL);
149 		virtio_register(
150 			fdt64_to_cpu(*(reg + 0)), fdt64_to_cpu(*(reg + 1)),
151 			GetInterrupt(fdt, node));
152 	} else if (
153 		strcmp(sUart.kind, "") == 0 && (
154 			HasFdtString(compatible, compatibleLen, "ns16550a") ||
155 			HasFdtString(compatible, compatibleLen, "sifive,uart0"))
156 	) {
157 		if (HasFdtString(compatible, compatibleLen, "ns16550a"))
158 			strcpy(sUart.kind, UART_KIND_8250);
159 		else if (HasFdtString(compatible, compatibleLen, "sifive,uart0"))
160 			strcpy(sUart.kind, UART_KIND_SIFIVE);
161 
162 		uint64* reg = (uint64*)fdt_getprop(fdt, node, "reg", NULL);
163 		sUart.regs.start = fdt64_to_cpu(*(reg + 0));
164 		sUart.regs.size  = fdt64_to_cpu(*(reg + 1));
165 		sUart.irq = GetInterrupt(fdt, node);
166 		const void* prop = fdt_getprop(fdt, node, "clock-frequency", NULL);
167 		sUart.clock = (prop == NULL) ? 0 : fdt32_to_cpu(*(uint32*)prop);
168 	} else if (HasFdtString(compatible, compatibleLen, "qemu,fw-cfg-mmio")) {
169 		gFwCfgRegs = (FwCfgRegs *volatile)
170 			fdt64_to_cpu(*(uint64*)fdt_getprop(fdt, node, "reg", NULL));
171 	} else if (HasFdtString(compatible, compatibleLen, "simple-framebuffer")) {
172 		gFramebuf.colors = (uint32*)fdt64_to_cpu(
173 			*(uint64*)fdt_getprop(fdt, node, "reg", NULL));
174 		gFramebuf.stride = fdt32_to_cpu(
175 			*(uint32*)fdt_getprop(fdt, node, "stride", NULL)) / 4;
176 		gFramebuf.width = fdt32_to_cpu(
177 			*(uint32*)fdt_getprop(fdt, node, "width", NULL));
178 		gFramebuf.height = fdt32_to_cpu(
179 			*(uint32*)fdt_getprop(fdt, node, "height", NULL));
180 	}
181 }
182 
183 
184 void
185 fdt_init(void* fdt)
186 {
187 	dprintf("FDT: %p\n", fdt);
188 	gFdt = fdt;
189 
190 	int res = fdt_check_header(gFdt);
191 	if (res != 0) {
192 		panic("Invalid FDT: %s\n", fdt_strerror(res));
193 	}
194 
195 	dprintf("FDT valid, size: %" B_PRIu32 "\n", fdt_totalsize(gFdt));
196 
197 	int node = -1;
198 	int depth = -1;
199 	while ((node = fdt_next_node(gFdt, node, &depth)) >= 0 && depth >= 0) {
200 		HandleFdt(gFdt, node, 2, 2, 1);
201 	}
202 }
203 
204 
205 void
206 fdt_set_kernel_args()
207 {
208 	uint32_t fdtSize = fdt_totalsize(gFdt);
209 
210 	// libfdt requires 8-byte alignment
211 	gKernelArgs.arch_args.fdt = (void*)(addr_t)kernel_args_malloc(fdtSize, 8);
212 
213 	if (gKernelArgs.arch_args.fdt != NULL)
214 		memcpy(gKernelArgs.arch_args.fdt, gFdt, fdt_totalsize(gFdt));
215 	else
216 		panic("unable to malloc for FDT!\n");
217 
218 	gKernelArgs.arch_args.timerFrequency = sTimerFrequrency;
219 
220 	gKernelArgs.arch_args.htif.start = (addr_t)gHtifRegs;
221 	gKernelArgs.arch_args.htif.size = sizeof(HtifRegs);
222 
223 	gKernelArgs.arch_args.plic  = sPlic;
224 	gKernelArgs.arch_args.clint = sClint;
225 	gKernelArgs.arch_args.uart  = sUart;
226 }
227