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