root/sys/arch/riscv/riscv/cpu.c
/*      $NetBSD: cpu.c,v 1.11 2026/03/18 06:41:41 skrll Exp $   */

/*-
 * Copyright (c) 2023 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Nick Hudson
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "opt_multiprocessor.h"

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.11 2026/03/18 06:41:41 skrll Exp $");

#include <sys/param.h>

#include <sys/cpu.h>
#include <sys/device.h>
#include <sys/kmem.h>
#include <sys/reboot.h>
#include <sys/sysctl.h>

#include <riscv/cpu.h>
#include <riscv/cpuvar.h>
#include <riscv/machdep.h>
#include <riscv/sbi.h>
#include <riscv/sysreg.h>

#ifdef MULTIPROCESSOR
#define NCPUINFO        MAXCPUS
#else
#define NCPUINFO        1
#endif /* MULTIPROCESSOR */

struct cpu_arch {
        uint64_t         ca_id;
        const char      *ca_name;
};

struct cpu_arch cpu_arch_sifive[] = {
    {
        .ca_id = CPU_SIFIVE_ARCH_7SERIES,
        .ca_name = "7-Series Processor (E7, S7, U7 series)",
    },
    { },        // terminator
};

struct cpu_arch cpu_arch_thead[] = {
    {
        .ca_id = 0,
        .ca_name = "9-Series Processor (C9, E9 series)",
    },
    { },        // terminator
};

struct cpu_vendor {
        uint32_t                 cv_id;
        const char              *cv_name;
        struct cpu_arch         *cv_arch;
} cpu_vendors[] = {
    {
        .cv_id = CPU_VENDOR_SIFIVE,
        .cv_name = "SiFive",
        .cv_arch = cpu_arch_sifive,
    },
    {
        .cv_id = CPU_VENDOR_THEAD,
        .cv_name = "T-Head",
        .cv_arch = cpu_arch_thead,
    },
};

/*
 * Our exported cpu_info structs; indexed by BP as 0 and APs [1, ncpu - 1]
 */
struct cpu_info cpu_info_store[NCPUINFO] = {
        [0] = {
                .ci_cpl = IPL_HIGH,
                .ci_curlwp = &lwp0,
                .ci_tlb_info = &pmap_tlb0_info,
#ifdef MULTIPROCESSOR
                .ci_flags = CPUF_PRIMARY | CPUF_PRESENT | CPUF_RUNNING,
#endif
        }
};

/*
 * setup the per-cpu sysctl tree.
 */
static void
cpu_setup_sysctl(device_t dv, struct cpu_info *ci)
{
        const struct sysctlnode *cpunode = NULL;

        sysctl_createv(NULL, 0, NULL, &cpunode,
                       CTLFLAG_PERMANENT,
                       CTLTYPE_NODE, device_xname(dv), NULL,
                       NULL, 0, NULL, 0,
                       CTL_MACHDEP,
                       CTL_CREATE, CTL_EOL);

        if (cpunode == NULL)
                return;
}


static void
cpu_identify(device_t self, struct cpu_info *ci)
{
        const register_t mvendorid = sbi_get_mvendorid().value;
        const register_t marchid = sbi_get_marchid().value;
        const uint32_t mimpid = sbi_get_mimpid().value;
        struct cpu_arch *cv_arch = NULL;
        const char *cv_name = NULL;
        const char *ca_name = NULL;
        char vendor[128];
        char arch[128];

        for (size_t i = 0; i < __arraycount(cpu_vendors); i++) {
                if (mvendorid == cpu_vendors[i].cv_id) {
                        cv_name = cpu_vendors[i].cv_name;
                        cv_arch = cpu_vendors[i].cv_arch;
                        break;
                }
        }

        if (cv_arch != NULL) {
                for (size_t i = 0; cv_arch[i].ca_name != NULL; i++) {
                        if (marchid == cv_arch[i].ca_id) {
                                ca_name = cv_arch[i].ca_name;
                                break;
                        }
                }
        }

        if (cv_name == NULL) {
                snprintf(vendor, sizeof(vendor), "vendor %" PRIxREGISTER, mvendorid);
                cv_name = vendor;
        }
        if (ca_name == NULL) {
                snprintf(arch, sizeof(arch), "arch %" PRIxREGISTER, marchid);
                ca_name = arch;
        }

        aprint_naive("\n");
        aprint_normal(": %s %s imp. %" PRIx32 "\n", cv_name, ca_name, mimpid);
        aprint_verbose_dev(ci->ci_dev,
            "vendor 0x%" PRIxREGISTER " arch. %" PRIxREGISTER " imp. %" PRIx32 "\n",
            mvendorid, marchid, mimpid);
}


void
cpu_attach(device_t dv, cpuid_t hartid)
{
        struct cpu_info *ci;

        /* Check for the BP */
        if (hartid == cpu_bphartid) {
                ci = curcpu();
                KASSERTMSG(ci == &cpu_info_store[0], "ci %p", ci);
                ci->ci_cpuid = hartid;
                ci->ci_cpu_freq = riscv_timer_frequency_get();
        } else {
#ifdef MULTIPROCESSOR
                if ((boothowto & RB_MD1) != 0) {
                        aprint_naive("\n");
                        aprint_normal(": multiprocessor boot disabled\n");
                        return;
                }

                KASSERT(hartid < MAXCPUS);
                KASSERT(cpu_hartindex[hartid] < MAXCPUS);

                ci = &cpu_info_store[cpu_hartindex[hartid]];

                ci->ci_cpl = IPL_HIGH;
                ci->ci_cpuid = hartid;

                if (!cpu_hatched_p(cpu_hartindex[hartid])) {
                        ci->ci_dev = dv;
                        device_set_private(dv, ci);
                        ci->ci_index = -1;

                        aprint_naive(": disabled\n");
                        aprint_normal(": disabled (unresponsive)\n");
                        return;
                }
#else /* MULTIPROCESSOR */
                aprint_naive(": disabled\n");
                aprint_normal(": disabled (uniprocessor kernel)\n");
                return;
#endif /* MULTIPROCESSOR */
        }

        ci->ci_dev = dv;
        device_set_private(dv, ci);

        const char * const xname = device_xname(dv);

        evcnt_attach_dynamic(&ci->ci_ev_fpu_loads, EVCNT_TYPE_MISC, NULL,
            xname, "fpu loads");
        evcnt_attach_dynamic(&ci->ci_ev_fpu_saves, EVCNT_TYPE_MISC, NULL,
            xname, "fpu saves");
        evcnt_attach_dynamic(&ci->ci_ev_fpu_reenables, EVCNT_TYPE_MISC, NULL,
            xname, "fpu reenables");

        cpu_identify(dv, ci);

#ifdef MULTIPROCESSOR
        kcpuset_create(&ci->ci_shootdowncpus, true);

        ipi_init(ci);

        kcpuset_create(&ci->ci_multicastcpus, true);
        kcpuset_create(&ci->ci_watchcpus, true);
        kcpuset_create(&ci->ci_ddbcpus, true);

        if (hartid != cpu_bphartid) {
                mi_cpu_attach(ci);
        }
#endif /* MULTIPROCESSOR */
        cpu_setup_sysctl(dv, ci);
}

#ifdef MULTIPROCESSOR
/*
 * Initialise a secondary processor.
 *
 * printf isn't available as kmutex(9) relies on curcpu which isn't setup yet.
 *
 */
void __noasan
cpu_init_secondary_processor(u_int cpuindex)
{
        cpu_set_hatched(cpuindex);

        /*
         * return to assembly to wait for cpu_boot_secondary_processors
         */
}


/*
 * When we are called, the MMU and caches are on and we are running on the stack
 * of the idlelwp for this cpu.
 */
void
cpu_hatch(struct cpu_info *ci, unsigned long cpuindex)
{
        KASSERT(curcpu() == ci);

        // Show this CPU as present.
        atomic_or_ulong(&ci->ci_flags, CPUF_PRESENT);

        ci->ci_cpu_freq = riscv_timer_frequency_get();

        riscv_timer_init();

        kcpuset_set(cpus_hatched, cpu_index(ci));
        kcpuset_set(cpus_running, cpu_index(ci));

        /*
         * clear my bit of the mailbox to tell cpu_boot_secondary_processors().
         * Consider that if there are cpu0, 1, 2, 3, and cpu2 is unresponsive,
         * ci_index for each would be cpu0=0, cpu1=1, cpu2=undef, cpu3=2.
         * therefore we have to use device_unit instead of ci_index for mbox.
         */

        cpu_clr_mbox(cpuindex);
}
#endif /* MULTIPROCESSOR */