* Copyright 2021, Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#include "fdt.h"
#include <SupportDefs.h>
#include <ByteOrder.h>
#include <KernelExport.h>
#include <boot/stage2.h>
#include <arch/generic/debug_uart_8250.h>
#include <arch/riscv64/arch_uart_sifive.h>
#include <arch_cpu_defs.h>
extern "C" {
#include <libfdt.h>
}
#include "mmu.h"
#include "smp.h"
#include "graphics.h"
#include "virtio.h"
#include "Htif.h"
#include "Clint.h"
#include "FwCfg.h"
void* gFdt = NULL;
ClintRegs *volatile gClintRegs = NULL;
static uint64 sTimerFrequrency = 10000000;
static addr_range sPlic = {0};
static addr_range sClint = {0};
static uart_info sUart{};
static bool
HasFdtString(const char* prop, int size, const char* pattern)
{
int patternLen = strlen(pattern);
const char* propEnd = prop + size;
while (propEnd - prop > 0) {
int curLen = strlen(prop);
if (curLen == patternLen && memcmp(prop, pattern, curLen + 1) == 0)
return true;
prop += curLen + 1;
}
return false;
}
static bool
GetReg(const void* fdt, int node, uint32 addressCells, uint32 sizeCells, size_t idx,
addr_range& range)
{
int propSize;
const uint8* prop = (const uint8*)fdt_getprop(fdt, node, "reg", &propSize);
if (prop == NULL)
return false;
size_t entrySize = 4*(addressCells + sizeCells);
if ((idx + 1)*entrySize > (size_t)propSize)
return false;
prop += idx*entrySize;
switch (addressCells) {
case 1: range.start = fdt32_to_cpu(*(uint32*)prop); prop += 4; break;
case 2: range.start = fdt64_to_cpu(*(uint64*)prop); prop += 8; break;
default: panic("unsupported addressCells");
}
switch (sizeCells) {
case 1: range.size = fdt32_to_cpu(*(uint32*)prop); prop += 4; break;
case 2: range.size = fdt64_to_cpu(*(uint64*)prop); prop += 8; break;
default: panic("unsupported sizeCells");
}
return true;
}
static uint32
GetInterrupt(const void* fdt, int node)
{
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", NULL)) {
return fdt32_to_cpu(*(prop + 1));
}
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts", NULL)) {
return fdt32_to_cpu(*prop);
}
dprintf("[!] no interrupt field\n");
return 0;
}
static void
HandleFdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells,
uint32 interruptCells )
{
const char* name = fdt_get_name(fdt, node, NULL);
if (strcmp(name, "cpus") == 0) {
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "timebase-frequency", NULL))
sTimerFrequrency = fdt32_to_cpu(*prop);
}
const char* device_type = (const char*)fdt_getprop(fdt, node,
"device_type", NULL);
if (device_type != NULL && strcmp(device_type, "memory") == 0) {
gMemBase = (uint8*)fdt64_to_cpu(*((uint64*)fdt_getprop(fdt, node,
"reg", NULL) + 0));
gTotalMem = fdt64_to_cpu(*((uint64*)fdt_getprop(fdt, node,
"reg", NULL) + 1));
return;
}
int compatibleLen;
const char* compatible = (const char*)fdt_getprop(fdt, node,
"compatible", &compatibleLen);
if (compatible == NULL) return;
if (HasFdtString(compatible, compatibleLen, "riscv,clint0")) {
uint64* reg = (uint64*)fdt_getprop(fdt, node, "reg", NULL);
sClint.start = fdt64_to_cpu(*(reg + 0));
sClint.size = fdt64_to_cpu(*(reg + 1));
gClintRegs = (ClintRegs*)sClint.start;
} else if (HasFdtString(compatible, compatibleLen, "riscv,plic0")) {
GetReg(fdt, node, addressCells, sizeCells, 0, sPlic);
int propSize;
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", &propSize)) {
dprintf("PLIC contexts\n");
uint32 contextId = 0;
for (uint32 *it = prop; (uint8_t*)it - (uint8_t*)prop < propSize; it += 2) {
uint32 phandle = fdt32_to_cpu(*it);
uint32 interrupt = fdt32_to_cpu(*(it + 1));
if (interrupt == sExternInt) {
CpuInfo* cpuInfo = smp_find_cpu(phandle);
dprintf(" context %" B_PRIu32 ": %" B_PRIu32 "\n", contextId, phandle);
if (cpuInfo != NULL) {
cpuInfo->plicContext = contextId;
dprintf(" hartId: %" B_PRIu32 "\n", cpuInfo->hartId);
}
}
contextId++;
}
}
} else if (HasFdtString(compatible, compatibleLen, "virtio,mmio")) {
uint64* reg = (uint64*)fdt_getprop(fdt, node, "reg", NULL);
virtio_register(
fdt64_to_cpu(*(reg + 0)), fdt64_to_cpu(*(reg + 1)),
GetInterrupt(fdt, node));
} else if (
strcmp(sUart.kind, "") == 0 && (
HasFdtString(compatible, compatibleLen, "ns16550a") ||
HasFdtString(compatible, compatibleLen, "sifive,uart0"))
) {
if (HasFdtString(compatible, compatibleLen, "ns16550a"))
strcpy(sUart.kind, UART_KIND_8250);
else if (HasFdtString(compatible, compatibleLen, "sifive,uart0"))
strcpy(sUart.kind, UART_KIND_SIFIVE);
uint64* reg = (uint64*)fdt_getprop(fdt, node, "reg", NULL);
sUart.regs.start = fdt64_to_cpu(*(reg + 0));
sUart.regs.size = fdt64_to_cpu(*(reg + 1));
sUart.irq = GetInterrupt(fdt, node);
const void* prop = fdt_getprop(fdt, node, "clock-frequency", NULL);
sUart.clock = (prop == NULL) ? 0 : fdt32_to_cpu(*(uint32*)prop);
} else if (HasFdtString(compatible, compatibleLen, "qemu,fw-cfg-mmio")) {
gFwCfgRegs = (FwCfgRegs *volatile)
fdt64_to_cpu(*(uint64*)fdt_getprop(fdt, node, "reg", NULL));
} else if (HasFdtString(compatible, compatibleLen, "simple-framebuffer")) {
gFramebuf.colors = (uint32*)fdt64_to_cpu(
*(uint64*)fdt_getprop(fdt, node, "reg", NULL));
gFramebuf.stride = fdt32_to_cpu(
*(uint32*)fdt_getprop(fdt, node, "stride", NULL)) / 4;
gFramebuf.width = fdt32_to_cpu(
*(uint32*)fdt_getprop(fdt, node, "width", NULL));
gFramebuf.height = fdt32_to_cpu(
*(uint32*)fdt_getprop(fdt, node, "height", NULL));
}
}
void
fdt_init(void* fdt)
{
dprintf("FDT: %p\n", fdt);
gFdt = fdt;
int res = fdt_check_header(gFdt);
if (res != 0) {
panic("Invalid FDT: %s\n", fdt_strerror(res));
}
dprintf("FDT valid, size: %" B_PRIu32 "\n", fdt_totalsize(gFdt));
int node = -1;
int depth = -1;
while ((node = fdt_next_node(gFdt, node, &depth)) >= 0 && depth >= 0) {
HandleFdt(gFdt, node, 2, 2, 1);
}
}
void
fdt_set_kernel_args()
{
uint32_t fdtSize = fdt_totalsize(gFdt);
gKernelArgs.arch_args.fdt = (void*)(addr_t)kernel_args_malloc(fdtSize, 8);
if (gKernelArgs.arch_args.fdt != NULL)
memcpy(gKernelArgs.arch_args.fdt, gFdt, fdt_totalsize(gFdt));
else
panic("unable to malloc for FDT!\n");
gKernelArgs.arch_args.timerFrequency = sTimerFrequrency;
gKernelArgs.arch_args.htif.start = (addr_t)gHtifRegs;
gKernelArgs.arch_args.htif.size = sizeof(HtifRegs);
gKernelArgs.arch_args.plic = sPlic;
gKernelArgs.arch_args.clint = sClint;
gKernelArgs.arch_args.uart = sUart;
}