#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>
#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:
printf("kernel: breakpoint\n");
break;
case -1:
printf("kernel: kdbint trap\n");
break;
default:
printf("kernel: cause %d\n", type);
if (db_recover != 0) {
db_error("Faulted in DDB; continuing...\n");
}
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 );
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
#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
show_cpuinfo(curcpu());
}
static void
db_mach_reset_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif)
{
}
#endif
#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
#endif