root/sys/arch/riscv/riscv/db_interface.c
/*      $NetBSD: db_interface.c,v 1.7 2026/05/22 06:15:01 skrll Exp $   */

/*
 * Mach Operating System
 * Copyright (c) 1991,1990 Carnegie Mellon University
 * All Rights Reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.7 2026/05/22 06:15:01 skrll Exp $");

#ifdef _KERNEL_OPT
#include "opt_multiprocessor.h"
#include "opt_kgdb.h"
#endif

#define __PMAP_PRIVATE

#include <sys/types.h>
#include <sys/param.h>

#include <sys/atomic.h>
#include <sys/cpu.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/systm.h>

#include <uvm/uvm_extern.h>

#include <dev/cons.h>

#include <machine/db_machdep.h>

#include <ddb/db_access.h>
#include <ddb/db_active.h>
#include <ddb/db_user.h>
#ifndef KGDB
#include <ddb/db_command.h>
#include <ddb/db_output.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#include <ddb/db_interface.h>
#include <ddb/db_lex.h>
#include <ddb/db_run.h> /* for db_continue_cmd() proto */
#endif

#define NOCPU   ~0
volatile u_int ddb_cpu = NOCPU;

int             db_active = 0;

#ifdef _KERNEL
db_regs_t       ddb_regs;

#ifdef MULTIPROCESSOR
static void db_mach_cpu_cmd(db_expr_t, bool, db_expr_t, const char *);
#endif
static void db_mach_reset_cmd(db_expr_t, bool, db_expr_t, const char *);

void db_cpuinfo_cmd(db_expr_t, bool, db_expr_t, const char *);
void db_tlbdump_cmd(db_expr_t, bool, db_expr_t, const char *);
void db_kvtophys_cmd(db_expr_t, bool, db_expr_t, const char *);

paddr_t kvtophys(vaddr_t);
#endif


const struct db_command db_machine_command_table[] = {
#ifdef _KERNEL
#ifdef MULTIPROCESSOR
        { DDB_ADD_CMD("cpu",    db_mach_cpu_cmd,        0,
                "switch to another cpu", "cpu#", NULL) },
#endif
        { DDB_ADD_CMD("cpuinfo", db_cpuinfo_cmd,        0,
                "Displays the cpuinfo",
                NULL, NULL)
        },
        { DDB_ADD_CMD("kvtop",  db_kvtophys_cmd,        0,
                "Print the physical address for a given kernel virtual address",
                "address",
                "   address:\tvirtual address to look up") },
        { DDB_ADD_CMD("reset",  db_mach_reset_cmd,      CS_NOREPEAT,
                "Initiate hardware reset",
                NULL, NULL) },
        { DDB_ADD_CMD("tlb",    db_tlbdump_cmd,         0,
                "Print out TLB entries. (only works with options DEBUG)",
                NULL, NULL) },
#endif
        { DDB_END_CMD },
};

#ifdef _KERNEL

#ifndef KGDB
int
kdb_trap(int type, db_regs_t *regs)
{
        switch (type) {
        case CAUSE_BREAKPOINT:  /* breakpoint */
                printf("kernel: breakpoint\n");
                break;
        case -1:                /* keyboard interrupt */
                printf("kernel: kdbint trap\n");
                break;
        default:
                printf("kernel: cause %d\n", type);
                if (db_recover != 0) {
                        db_error("Faulted in DDB; continuing...\n");
                        /*NOTREACHED*/
                }
                break;
        }

        const int s = splhigh();
        struct cpu_info * const ci = curcpu();

#if defined(MULTIPROCESSOR)
        bool first_in_ddb = false;
        const u_int cpu_me = cpu_index(ci);
        const u_int old_ddb_cpu = atomic_cas_uint(&ddb_cpu, NOCPU, cpu_me);
        if (old_ddb_cpu == NOCPU) {
                first_in_ddb = true;
                cpu_pause_others();
        } else {
                if (old_ddb_cpu != cpu_me) {
                        KASSERT(cpu_is_paused(cpu_me));
                        cpu_pause();
                        splx(s);
                        return 1;
                }
        }
        KASSERT(!cpu_is_paused(cpu_me));
#endif

        ddb_regs = *regs;
        ci->ci_ddb_regs = &ddb_regs;
        db_active++;
        cnpollc(true);
        db_trap(type, 0 /*code*/);
        cnpollc(false);
        db_active--;
        ci->ci_ddb_regs = NULL;
        *regs = ddb_regs;

#if defined(MULTIPROCESSOR)
        if (atomic_cas_uint(&ddb_cpu, cpu_me, NOCPU) == cpu_me) {
                cpu_resume_others();
        } else {
                cpu_resume(ddb_cpu);
                if (first_in_ddb)
                        cpu_pause();
        }
#endif

        splx(s);
        return 1;
}
#endif  /* !KGDB */


#ifndef KGDB
void
db_tlbdump_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
               const char *modif)
{
}

void
db_kvtophys_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
                const char *modif)
{

        if (!have_addr)
                return;
}


static void
show_cpuinfo(struct cpu_info *kci)
{
        struct cpu_info cpuinfobuf;
        cpuid_t cpuid;
        int i;

        db_read_bytes((db_addr_t)kci, sizeof(cpuinfobuf), (char *)&cpuinfobuf);

        struct cpu_info *ci = &cpuinfobuf;
        cpuid = ci->ci_cpuid;
        db_printf("cpu_info=%p, cpu_name=%s\n", kci, ci->ci_cpuname);
        db_printf("%p cpu[%lu].ci_cpuid              = %lu\n",
            &ci->ci_cpuid, cpuid, ci->ci_cpuid);
        db_printf("%p cpu[%lu].ci_curlwp             = %p\n",
            &ci->ci_curlwp, cpuid, ci->ci_curlwp);
        for (i = 0; i < SOFTINT_COUNT; i++) {
                db_printf("%p cpu[%lu].ci_softlwps[%d]        = %p\n",
                    &ci->ci_softlwps[i], cpuid, i, ci->ci_softlwps[i]);
        }
        db_printf("%p cpu[%lu].ci_want_resched       = %d\n",
            &ci->ci_want_resched, cpuid, ci->ci_want_resched);
        db_printf("%p cpu[%lu].ci_cpl                = %d\n",
            &ci->ci_cpl, cpuid, ci->ci_cpl);
        db_printf("%p cpu[%lu].ci_softints           = 0x%08x\n",
            &ci->ci_softints, cpuid, ci->ci_softints);
        db_printf("%p cpu[%lu].ci_intr_depth         = %u\n",
            &ci->ci_intr_depth, cpuid, ci->ci_intr_depth);
        db_printf("%p cpu[%lu].ci_lastintr           = %" PRIu64 "\n",
            &ci->ci_lastintr, cpuid, ci->ci_lastintr);
        db_printf("%p cpu[%lu].ci_lastintr_scheduled = %" PRIu64 "\n",
            &ci->ci_lastintr_scheduled, cpuid, ci->ci_lastintr_scheduled);
}

void
db_cpuinfo_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
    const char *modif)
{
#ifdef MULTIPROCESSOR
        CPU_INFO_ITERATOR cii;
        struct cpu_info *ci;
        bool showall = false;

        if (modif != NULL) {
                for (; *modif != '\0'; modif++) {
                        switch (*modif) {
                        case 'a':
                                showall = true;
                                break;
                        }
                }
        }

        if (showall) {
                for (CPU_INFO_FOREACH(cii, ci)) {
                        show_cpuinfo(ci);
                }
        } else
#endif /* MULTIPROCESSOR */
                show_cpuinfo(curcpu());
}


static void
db_mach_reset_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
                const char *modif)
{
}

#endif  /* !KGDB */

#ifdef MULTIPROCESSOR

bool
ddb_running_on_this_cpu_p(void)
{
        return ddb_cpu == cpu_index(curcpu());
}

bool
ddb_running_on_any_cpu_p(void)
{
        return ddb_cpu != NOCPU;
}

void
db_resume_others(void)
{
        u_int cpu_me = cpu_index(curcpu());

        if (atomic_cas_uint(&ddb_cpu, cpu_me, NOCPU) == cpu_me)
                cpu_resume_others();
}

static void
db_mach_cpu_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
{
        CPU_INFO_ITERATOR cii;
        struct cpu_info *ci;

        if (!have_addr) {
                cpu_debug_dump();
                return;
        }
        for (CPU_INFO_FOREACH(cii, ci)) {
                if (cpu_index(ci) == addr)
                        break;
        }
        if (ci == NULL) {
                db_printf("CPU %ld not configured\n", (long)addr);
                return;
        }
        if (ci != curcpu()) {
                if (!cpu_is_paused(cpu_index(ci))) {
                        db_printf("CPU %ld not paused\n", (long)addr);
                        return;
                }
                (void)atomic_cas_uint(&ddb_cpu, cpu_index(curcpu()), cpu_index(ci));
                db_continue_cmd(0, false, 0, "");
        }
}
#endif  /* MULTIPROCESSOR */

#endif  /* _KERNEL */