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
16extern "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
29void* gFdt = NULL;
30ClintRegs *volatile gClintRegs = NULL;
31
32static uint64 sTimerFrequrency = 10000000;
33
34static addr_range sPlic = {0};
35static addr_range sClint = {0};
36static uart_info sUart{};
37
38
39static bool
40HasFdtString(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
54static bool
55GetReg(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
83static uint32
84GetInterrupt(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
97static void
98HandleFdt(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
184void
185fdt_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
205void
206fdt_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