#include "opt_multiprocessor.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.5 2026/01/15 18:09:26 skrll Exp $");
#include <sys/param.h>
#include <sys/cpu.h>
#include <dev/fdt/fdtvar.h>
#include <riscv/cpufunc.h>
#include <riscv/cpuvar.h>
#include <riscv/machdep.h>
#include <riscv/sbi.h>
#include <riscv/fdt/riscv_fdtvar.h>
bool
riscv_fdt_cpu_okay(const int node)
{
const char *s;
s = fdtbus_get_string(node, "device_type");
if (!s || strcmp(s, "cpu") != 0)
return false;
s = fdtbus_get_string(node, "status");
if (s) {
if (strcmp(s, "okay") == 0)
return true;
if (strcmp(s, "disabled") == 0)
return false;
return false;
} else {
return true;
}
}
void
riscv_fdt_cpu_bootstrap(void)
{
const int cpus = OF_finddevice("/cpus");
if (cpus == -1) {
aprint_error("%s: no /cpus node found\n", __func__);
return;
}
u_int cpuindex = 1;
for (int child = OF_child(cpus); child; child = OF_peer(child)) {
if (!riscv_fdt_cpu_okay(child))
continue;
uint64_t reg;
if (fdtbus_get_reg64(child, 0, ®, NULL) != 0)
continue;
const cpuid_t hartid = reg;
if (hartid > MAXCPUS) {
aprint_error("hart id too big %lu (%u)", hartid,
MAXCPUS);
continue;
}
struct sbiret sbiret = sbi_hart_get_status(hartid);
switch (sbiret.error) {
case SBI_ERR_INVALID_PARAM:
aprint_error("Unknown hart id %lx", hartid);
continue;
case SBI_SUCCESS:
break;
default:
aprint_error("Unexpected error (%ld) from get_status",
sbiret.error);
}
if (sbiret.value == SBI_HART_STARTED) {
if (cpu_bphartid != ~0UL) {
panic("more than 1 hart started");
}
cpu_bphartid = hartid;
cpu_hartindex[hartid] = 0;
continue;
}
KASSERT(cpuindex < MAXCPUS);
cpu_hartindex[hartid] = cpuindex++;
}
}
int
riscv_fdt_cpu_mpstart(void)
{
int ret = 0;
#ifdef MULTIPROCESSOR
const int cpus = OF_finddevice("/cpus");
if (cpus == -1) {
aprint_error("%s: no /cpus node found\n", __func__);
return 0;
}
u_int cpuindex = 1;
for (int child = OF_child(cpus); child; child = OF_peer(child)) {
if (!riscv_fdt_cpu_okay(child))
continue;
uint64_t reg;
if (fdtbus_get_reg64(child, 0, ®, NULL) != 0)
continue;
const cpuid_t hartid = reg;
if (hartid == cpu_bphartid)
continue;
const paddr_t entry = KERN_VTOPHYS(cpu_mpstart);
struct sbiret sbiret = sbi_hart_start(hartid, entry, cpuindex);
switch (sbiret.error) {
case SBI_SUCCESS:
break;
case SBI_ERR_INVALID_ADDRESS:
break;
case SBI_ERR_INVALID_PARAM:
break;
case SBI_ERR_ALREADY_AVAILABLE:
break;
case SBI_ERR_FAILED:
break;
default:
aprint_error("%s: failed to enable CPU %#lx\n",
__func__, hartid);
}
size_t i;
for (i = 0x10000000; i > 0; i--) {
if (cpu_hatched_p(cpuindex))
break;
}
if (i == 0) {
ret++;
aprint_error("hart%ld: WARNING: AP %u failed to start\n",
hartid, cpuindex);
}
cpuindex++;
}
#else
aprint_normal("%s: kernel compiled without MULTIPROCESSOR\n", __func__);
#endif
return ret;
}
static int
cpu_fdt_match(device_t parent, cfdata_t cf, void *aux)
{
struct fdt_attach_args * const faa = aux;
const int phandle = faa->faa_phandle;
const char *device_type;
device_type = fdtbus_get_string(phandle, "device_type");
return device_type != NULL && strcmp(device_type, "cpu") == 0;
}
static void
cpu_fdt_attach(device_t parent, device_t self, void *aux)
{
struct fdt_attach_args * const faa = aux;
const int phandle = faa->faa_phandle;
bus_addr_t hartid;
if (fdtbus_get_reg(phandle, 0, &hartid, NULL) != 0)
hartid = 0;
cpu_attach(self, hartid);
fdt_add_bus(self, phandle, faa);
}
CFATTACH_DECL_NEW(cpu_fdt, 0, cpu_fdt_match, cpu_fdt_attach, NULL, NULL);