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
HasFdtString(const char * prop,int size,const char * pattern)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
GetReg(const void * fdt,int node,uint32 addressCells,uint32 sizeCells,size_t idx,addr_range & range)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
GetInterrupt(const void * fdt,int node)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
HandleFdt(const void * fdt,int node,uint32 addressCells,uint32 sizeCells,uint32 interruptCells)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
fdt_init(void * fdt)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
fdt_set_kernel_args()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