/*	$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 */
