#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.101 2026/02/25 05:34:42 skrll Exp $");
#ifdef _KERNEL_OPT
#include "opt_multiprocessor.h"
#include "opt_cputype.h"
#include "opt_ddb.h"
#include "opt_kgdb.h"
#endif
#define __PMAP_PRIVATE
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/atomic.h>
#include <sys/cpu.h>
#include <uvm/uvm_extern.h>
#include <mips/regnum.h>
#include <mips/cache.h>
#include <mips/pcb.h>
#include <mips/pte.h>
#include <mips/locore.h>
#include <mips/mips_opcode.h>
#include <dev/cons.h>
#include <machine/int_fmtio.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;
#ifdef DDB
int db_active = 0;
#endif
#ifdef _KERNEL
db_regs_t ddb_regs;
#endif
#if (MIPS32 + MIPS32R2 + MIPS64 + MIPS64R2) > 0
static void db_watch_cmd(db_expr_t, bool, db_expr_t, const char *);
static void db_unwatch_cmd(db_expr_t, bool, db_expr_t, const char *);
#endif
#ifdef MULTIPROCESSOR
static void db_mach_cpu_cmd(db_expr_t, bool, db_expr_t, const char *);
#endif
void db_cp0dump_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_kvtophys_cmd(db_expr_t, bool, db_expr_t, const char *);
void db_tlbdump_cmd(db_expr_t, bool, db_expr_t, const char *);
#ifdef MIPS64_XLS
void db_mfcr_cmd(db_expr_t, bool, db_expr_t, const char *);
void db_mtcr_cmd(db_expr_t, bool, db_expr_t, const char *);
#endif
paddr_t kvtophys(vaddr_t);
#ifdef _KERNEL
CTASSERT(sizeof(ddb_regs) == sizeof(struct reg));
#ifndef KGDB
int
kdb_trap(int type, struct reg *regs)
{
int s;
switch (type) {
case T_WATCH:
case T_BREAK:
printf("kernel: %s trap\n", trap_names[type & 0x1f]);
break;
case -1:
printf("kernel: kdbint trap\n");
break;
default:
printf("kernel: %s trap\n", trap_names[type & 0x1f]);
if (db_recover != 0) {
db_error("Faulted in DDB; continuing...\n");
}
break;
}
s = splhigh();
#if defined(MULTIPROCESSOR)
bool first_in_ddb = false;
const u_int cpu_me = cpu_number();
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(regs);
splx(s);
return 1;
}
}
KASSERT(! cpu_is_paused(cpu_me));
#endif
ddb_regs = *regs;
db_active++;
cnpollc(true);
db_trap(type & ~T_USER, 0 );
cnpollc(false);
db_active--;
*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(regs);
}
#endif
splx(s);
return 1;
}
void
cpu_Debugger(void)
{
__asm("break");
}
#endif
void
db_read_bytes(vaddr_t addr, size_t size, char *data)
{
const char *src = (char *)addr;
int err;
if (addr < VM_MAXUSER_ADDRESS) {
err = copyin(src, data, size);
if (err) {
#ifdef DDB
db_printf("address %p is invalid\n", src);
#endif
memset(data, 0, size);
}
return;
}
if (size <= 8 && (size & (size-1)) == 0 && (addr & (size-1)) == 0
&& ((uintptr_t)data & (size-1)) == 0) {
if (size == sizeof(uint8_t))
*(uint8_t *)data = *(const uint8_t *)src;
else if (size == sizeof(uint16_t))
*(uint16_t *)data = *(const uint16_t *)src;
else if (size == sizeof(uint32_t))
*(uint32_t *)data = *(const uint32_t *)src;
else
*(uint64_t *)data = *(const uint64_t *)src;
return;
}
while (size--)
*data++ = *src++;
}
void
db_write_bytes(vaddr_t addr, size_t size, const char *data)
{
char *p = (char *)addr;
size_t n = size;
int err;
if (addr < VM_MAXUSER_ADDRESS) {
err = copyout(data, p, size);
if (err) {
#ifdef DDB
db_printf("address %p is invalid\n", p);
#endif
}
return;
}
if (size <= 8 && (size & (size-1)) == 0 && (addr & (size-1)) == 0
&& ((uintptr_t)data & (size-1)) == 0) {
if (size == sizeof(uint8_t))
*(uint8_t *)p = *(const uint8_t *)data;
else if (size == sizeof(uint16_t))
*(uint16_t *)p = *(const uint16_t *)data;
else if (size == sizeof(uint32_t))
*(uint32_t *)p = *(const uint32_t *)data;
else
*(uint64_t *)p = *(const uint64_t *)data;
return;
}
while (n--)
*p++ = *data++;
wbflush();
mips_icache_sync_range(addr, size);
}
#ifndef KGDB
void
db_tlbdump_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif)
{
struct tlbmask tlb;
bool valid_only = false;
if (modif[0] == 'v')
valid_only = true;
#ifdef MIPS1
if (!MIPS_HAS_R4K_MMU) {
int i;
for (i = 0; i < mips_options.mips_num_tlb_entries; i++) {
tlb_read_entry(i, &tlb);
if (valid_only && !(tlb.tlb_lo1 & MIPS1_PG_V))
continue;
db_printf("TLB%c%2d Hi 0x%08x Lo 0x%08x",
(tlb.tlb_lo1 & MIPS1_PG_V) ? ' ' : '*',
i, tlb.tlb_hi,
tlb.tlb_lo1 & MIPS1_PG_FRAME);
db_printf(" %c%c%c\n",
(tlb.tlb_lo1 & MIPS1_PG_D) ? 'D' : ' ',
(tlb.tlb_lo1 & MIPS1_PG_G) ? 'G' : ' ',
(tlb.tlb_lo1 & MIPS1_PG_N) ? 'N' : ' ');
}
}
#endif
#ifdef MIPS3_PLUS
if (MIPS_HAS_R4K_MMU) {
int i;
const int tlb_count_width =
mips_options.mips_num_tlb_entries > 100 ? 3 : 2;
for (i = 0; i < mips_options.mips_num_tlb_entries; i++) {
tlb_read_entry(i, &tlb);
if (valid_only &&
!((tlb.tlb_lo0 | tlb.tlb_lo1) & MIPS3_PG_V))
continue;
db_printf("TLB%c%*d Hi 0x%08"PRIxVADDR" ",
(tlb.tlb_lo0 | tlb.tlb_lo1) & MIPS3_PG_V ? ' ' : '*',
tlb_count_width, i, tlb.tlb_hi);
db_printf("Lo0=0x%09" PRIx64 " %c%c attr %x ",
(uint64_t)mips_tlbpfn_to_paddr(tlb.tlb_lo0),
(tlb.tlb_lo0 & MIPS3_PG_D) ? 'D' : ' ',
(tlb.tlb_lo0 & MIPS3_PG_G) ? 'G' : ' ',
(int)(tlb.tlb_lo0 >> 3) & 7);
db_printf("Lo1=0x%09" PRIx64 " %c%c attr %x sz=%x\n",
(uint64_t)mips_tlbpfn_to_paddr(tlb.tlb_lo1),
(tlb.tlb_lo1 & MIPS3_PG_D) ? 'D' : ' ',
(tlb.tlb_lo1 & MIPS3_PG_G) ? 'G' : ' ',
(int)(tlb.tlb_lo1 >> 3) & 7,
tlb.tlb_mask);
}
}
#endif
}
void
db_kvtophys_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif)
{
if (!have_addr)
return;
if (VM_MIN_KERNEL_ADDRESS <= addr && addr < VM_MAX_KERNEL_ADDRESS) {
db_printf("0x%" DDB_EXPR_FMT "x -> 0x%" PRIx64 "\n", addr,
(uint64_t) kvtophys(addr));
} else
db_printf("not a kernel virtual address\n");
}
#define FLDWIDTH 10
#define SHOW32(reg, name) SHOW32SELECT(reg, 0, name)
#define SHOW64(reg, name) SHOW64SELECT(reg, 0, name)
#define SHOW32SEL(reg, name) SHOW32SELECT(reg, name)
#define SHOW64SEL(reg, name) SHOW64SELECT(reg, name)
#define SHOW32SELECT(num, sel, name) \
do { \
uint32_t __val; \
\
__asm volatile( \
".set push \n\t" \
".set mips32 \n\t" \
"mfc0 %0,$%1,%2 \n\t" \
".set pop \n\t" \
: "=r"(__val) : "n"(num), "n"(sel)); \
db_printf(" %s:%*s %#x\n", name, \
FLDWIDTH - (int) strlen(name), "", __val); \
} while (0)
#define SHOW64SELECT(num, sel, name) \
do { \
uint64_t __val; \
\
KASSERT (CPUIS64BITS); \
__asm volatile( \
".set push \n\t" \
".set mips64 \n\t" \
".set noat \n\t" \
"dmfc0 %0,$%1,%2 \n\t" \
".set pop" \
: "=r"(__val) : "n"(num), "n"(sel)); \
db_printf(" %s:%*s %#"PRIx64"\n", name, \
FLDWIDTH - (int) strlen(name), "", __val); \
} while (0)
#define SET32(reg, name, val) \
do { \
\
__asm volatile("mtc0 %0,$" ___STRING(reg) :: "r"(val)); \
if (name != NULL) \
db_printf(" %s =%*s %#x\n", name, \
FLDWIDTH - (int) strlen(name), "", val); \
} while (0)
#define SET64(reg, name) MIPS64_SET64(reg, 0, name)
#define MIPS64_SET32(num, sel, name, val) \
do { \
\
__asm volatile( \
".set push \n\t" \
".set mips32 \n\t" \
"mtc0 %0,$%1,%2 \n\t" \
".set pop \n\t" \
:: "r"(val), "n"(num), "n"(sel)); \
if (name != NULL) \
db_printf(" %s =%*s %#x\n", name, \
FLDWIDTH - (int) strlen(name), "", val); \
} while (0)
#define MIPS64_SET64(num, sel, name, val) \
do { \
\
KASSERT (CPUIS64BITS); \
__asm volatile( \
".set push \n\t" \
".set mips64 \n\t" \
".set noat \n\t" \
"dmtc0 %0,$%1,%2 \n\t" \
".set pop" \
:: "r"(val), "n"(num), "n"(sel)); \
if (name != NULL) \
db_printf(" %s =%*s %#"PRIx64"\n", name, \
FLDWIDTH - (int) strlen(name), "", (uint64_t)val); \
} while (0)
void
db_cp0dump_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif)
{
u_int cp0flags = mips_options.mips_cpu->cpu_cp0flags;
SHOW32(MIPS_COP_0_TLB_INDEX, "index");
SHOW32(MIPS_COP_0_TLB_RANDOM, "random");
if (!MIPS_HAS_R4K_MMU) {
SHOW32(MIPS_COP_0_TLB_LOW, "entrylow");
} else {
if (CPUIS64BITS) {
SHOW64(MIPS_COP_0_TLB_LO0, "entrylo0");
SHOW64(MIPS_COP_0_TLB_LO1, "entrylo1");
} else {
SHOW32(MIPS_COP_0_TLB_LO0, "entrylo0");
SHOW32(MIPS_COP_0_TLB_LO1, "entrylo1");
}
}
if (CPUIS64BITS) {
SHOW64(MIPS_COP_0_TLB_CONTEXT, "context");
} else {
SHOW32(MIPS_COP_0_TLB_CONTEXT, "context");
}
if (MIPS_HAS_R4K_MMU) {
SHOW32(MIPS_COP_0_TLB_PG_MASK, "pagemask");
SHOW32(MIPS_COP_0_TLB_WIRED, "wired");
}
if (CPUIS64BITS) {
SHOW64(MIPS_COP_0_BAD_VADDR, "badvaddr");
} else {
SHOW32(MIPS_COP_0_BAD_VADDR, "badvaddr");
}
if (mips_options.mips_cpu_arch >= CPU_ARCH_MIPS3) {
SHOW32(MIPS_COP_0_COUNT, "count");
}
if ((cp0flags & MIPS_CP0FL_USE) != 0) {
if ((cp0flags & MIPS_CP0FL_EIRR) != 0)
SHOW64SEL(MIPS_COP_0_EIRR, "eirr");
if ((cp0flags & MIPS_CP0FL_EIMR) != 0)
SHOW64SEL(MIPS_COP_0_EIMR, "eimr");
}
if (CPUIS64BITS) {
SHOW64(MIPS_COP_0_TLB_HI, "entryhi");
} else {
SHOW32(MIPS_COP_0_TLB_HI, "entryhi");
}
if (mips_options.mips_cpu_arch >= CPU_ARCH_MIPS3) {
SHOW32(MIPS_COP_0_COMPARE, "compare");
}
SHOW32(MIPS_COP_0_STATUS, "status");
SHOW32(MIPS_COP_0_CAUSE, "cause");
if (CPUIS64BITS) {
SHOW64(MIPS_COP_0_EXC_PC, "epc");
} else {
SHOW32(MIPS_COP_0_EXC_PC, "epc");
}
SHOW32(MIPS_COP_0_PRID, "prid");
if ((cp0flags & MIPS_CP0FL_USE) != 0) {
if ((cp0flags & MIPS_CP0FL_EBASE) != 0)
SHOW32SEL(MIPS_COP_0_EBASE, "ebase");
if ((cp0flags & MIPS_CP0FL_CONFIG) != 0)
SHOW32(MIPS_COP_0_CONFIG, "config");
if ((cp0flags & MIPS_CP0FL_CONFIG1) != 0)
SHOW32SEL(MIPS_COP_0_CONFIG1, "config1");
if ((cp0flags & MIPS_CP0FL_CONFIG2) != 0)
SHOW32SEL(MIPS_COP_0_CONFIG2, "config2");
if ((cp0flags & MIPS_CP0FL_CONFIG3) != 0)
SHOW32SEL(MIPS_COP_0_CONFIG3, "config3");
if ((cp0flags & MIPS_CP0FL_CONFIG4) != 0)
SHOW32SEL(MIPS_COP_0_CONFIG4, "config4");
if ((cp0flags & MIPS_CP0FL_CONFIG5) != 0)
SHOW32SEL(MIPS_COP_0_CONFIG5, "config5");
if ((cp0flags & MIPS_CP0FL_CONFIG6) != 0)
SHOW32SEL(MIPS_COP_0_CONFIG6, "config6");
if ((cp0flags & MIPS_CP0FL_CONFIG7) != 0)
SHOW32SEL(MIPS_COP_0_CONFIG7, "config7");
if (CPUISMIPSNNR2)
SHOW32(MIPS_COP_0_HWRENA, "hwrena");
if (MIPS_HAS_USERLOCAL)
SHOW32SEL(MIPS_COP_0_USERLOCAL, "userlocal");
} else {
SHOW32(MIPS_COP_0_CONFIG, "config");
#if (MIPS32 + MIPS32R2 + MIPS64 + MIPS64R2) > 0
if (CPUISMIPSNN) {
uint32_t val;
val = mipsNN_cp0_config1_read();
db_printf(" config1: %#x\n", val);
}
#endif
}
if (MIPS_HAS_LLSC) {
if (MIPS_HAS_LLADDR) {
if (CPUIS64BITS)
SHOW64(MIPS_COP_0_LLADDR, "lladdr");
else
SHOW32(MIPS_COP_0_LLADDR, "lladdr");
}
}
#if (MIPS32 + MIPS32R2 + MIPS64 + MIPS64R2) > 0
for (int i=0; i < curcpu()->ci_cpuwatch_count; i++) {
const intptr_t lo = mipsNN_cp0_watchlo_read(i);
const uint32_t hi = mipsNN_cp0_watchhi_read(i);
db_printf(" %s%d:%*s %#" PRIxPTR "\t",
"watchlo", i, FLDWIDTH - 8, "", lo);
db_printf(" %s%d:%*s %#" PRIx32 "\n",
"watchhi", i, FLDWIDTH - 8, "", hi);
}
#endif
if (CPUIS64BITS) {
SHOW64(MIPS_COP_0_TLB_XCONTEXT, "xcontext");
}
if (CPUISMIPSNN) {
if (CPUIS64BITS) {
SHOW64(MIPS_COP_0_PERFCNT0_CTL, "perfcnt0ctl");
SHOW64SEL(MIPS_COP_0_PERFCNT0_CNT, "perfcnt0cnt");
} else {
SHOW32(MIPS_COP_0_PERFCNT0_CTL, "perfcnt0ctl");
SHOW32SEL(MIPS_COP_0_PERFCNT0_CNT, "perfcnt0cnt");
}
}
if (((cp0flags & MIPS_CP0FL_USE) == 0) ||
((cp0flags & MIPS_CP0FL_ECC) != 0))
SHOW32(MIPS_COP_0_ECC, "ecc");
if (((cp0flags & MIPS_CP0FL_USE) == 0) ||
((cp0flags & MIPS_CP0FL_CACHE_ERR) != 0))
SHOW32(MIPS_COP_0_CACHE_ERR, "cacherr");
SHOW32(MIPS_COP_0_TAG_LO, "cachelo");
SHOW32(MIPS_COP_0_TAG_HI, "cachehi");
if (CPUIS64BITS) {
SHOW64(MIPS_COP_0_ERROR_PC, "errorpc");
} else {
SHOW32(MIPS_COP_0_ERROR_PC, "errorpc");
}
}
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_idepth = %u\n",
&ci->ci_idepth, cpuid, ci->ci_idepth);
}
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());
}
#if (MIPS32 + MIPS32R2 + MIPS64 + MIPS64R2) > 0
static void
db_watch_cmd(db_expr_t address, bool have_addr, db_expr_t count,
const char *modif)
{
struct cpu_info * const ci = curcpu();
cpu_watchpoint_t *cwp;
register_t mask=0;
uint32_t asid;
uint32_t mode;
db_expr_t value;
char str[6];
if (!have_addr) {
db_printf("%-3s %-5s %-16s %4s %4s\n",
"#", "MODE", "ADDR", "MASK", "ASID");
for (u_int i=0; i < ci->ci_cpuwatch_count; i++) {
cwp = &ci->ci_cpuwatch_tab[i];
mode = cwp->cw_mode;
if ((mode & CPUWATCH_RWX) == 0)
continue;
str[0] = (mode & CPUWATCH_READ) ? 'r' : '-';
str[1] = (mode & CPUWATCH_WRITE) ? 'w' : '-';
str[2] = (mode & CPUWATCH_EXEC) ? 'x' : '-';
str[3] = (mode & CPUWATCH_MASK) ? 'm' : '-';
str[4] = (mode & CPUWATCH_ASID) ? 'a' : 'g';
str[5] = '\0';
db_printf("%2u: %s %16" PRIxREGISTER
" %4" PRIxREGISTER " %4x\n",
i, str, cwp->cw_addr, cwp->cw_mask, cwp->cw_asid);
}
db_flush_lex();
return;
}
cwp = cpuwatch_alloc();
if (cwp == NULL) {
db_printf("no watchpoint available\n");
db_flush_lex();
return;
}
KASSERT(modif != NULL);
mode = 0;
for (int i=0; modif[i] != '\0'; i++) {
switch(modif[i]) {
case 'w':
mode |= CPUWATCH_WRITE;
break;
case 'm':
mode |= CPUWATCH_MASK;
break;
case 'r':
mode |= CPUWATCH_READ;
break;
case 'x':
mode |= CPUWATCH_EXEC;
break;
case 'a':
mode |= CPUWATCH_ASID;
break;
}
}
if (mode == 0) {
db_printf("mode modifier(s) missing\n");
db_flush_lex();
return;
}
if (mode & CPUWATCH_MASK) {
if (! db_expression(&value)) {
db_printf("mask missing\n");
db_flush_lex();
return;
}
mask = (register_t)(value & __BITS(11, 3));
}
if (mode & CPUWATCH_ASID) {
if (! db_expression(&value)) {
db_printf("asid missing\n");
db_flush_lex();
return;
}
asid = (uint32_t)(value & __BITS(7,0));
} else {
asid = 0;
}
if (mode & (CPUWATCH_MASK|CPUWATCH_ASID))
db_skip_to_eol();
else
db_flush_lex();
cwp->cw_addr = (register_t)address;
cwp->cw_mask = (register_t)mask;
cwp->cw_asid = asid;
cwp->cw_mode = mode;
cpuwatch_set(cwp);
}
static void
db_unwatch_cmd(db_expr_t address, bool have_addr, db_expr_t count,
const char *modif)
{
struct cpu_info * const ci = curcpu();
const bool unwatch_all = !have_addr;
int n;
n = 0;
for (u_int i=0; i < ci->ci_cpuwatch_count; i++) {
cpu_watchpoint_t * const cwp = &ci->ci_cpuwatch_tab[i];
if (unwatch_all || (cwp->cw_addr == (register_t)address)) {
cpuwatch_free(cwp);
n++;
}
}
if (n == 0)
db_printf("no watch found on address %#" PRIxREGISTER "\n",
(register_t)address);
}
#endif
#ifdef MIPS64_XLS
void
db_mfcr_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif)
{
uint64_t value;
if ((mips_options.mips_cpu->cpu_flags & CPU_MIPS_HAVE_MxCR) == 0) {
db_printf("mfcr not implemented on this CPU\n");
return;
}
if (!have_addr) {
db_printf("Address missing\n");
return;
}
__asm volatile( \
".set push \n\t" \
".set arch=xlr \n\t" \
".set noat \n\t" \
"mfcr %0,%1 \n\t" \
".set pop \n\t" \
: "=r"(value) : "r"(addr));
db_printf("control reg 0x%" DDB_EXPR_FMT "x = 0x%" PRIx64 "\n",
addr, value);
}
void
db_mtcr_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif)
{
db_expr_t value;
if ((mips_options.mips_cpu->cpu_flags & CPU_MIPS_HAVE_MxCR) == 0) {
db_printf("mtcr not implemented on this CPU\n");
return;
}
if ((!have_addr) || (! db_expression(&value))) {
db_printf("Address missing\n");
db_flush_lex();
return;
}
db_skip_to_eol();
__asm volatile( \
".set push \n\t" \
".set arch=xlr \n\t" \
".set noat \n\t" \
"mtcr %0,%1 \n\t" \
".set pop \n\t" \
:: "r"(value), "r"(addr));
db_printf("control reg 0x%" DDB_EXPR_FMT "x = 0x%" DDB_EXPR_FMT "x\n",
addr, value);
}
#endif
#ifdef MIPS64_OCTEON
#include <mips/cavium/dev/octeon_ciureg.h>
#ifdef MULTIPROCESSOR
static void
db_mach_nmi_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) {
db_printf("CPU not specific\n");
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()) {
db_printf("CPU %ld is current cpu; request ignored\n",
(long)addr);
return;
}
mips3_sd(MIPS_PHYS_TO_XKPHYS_UNCACHED(CIU_NMI), __BIT(ci->ci_cpuid));
}
#endif
#endif
static void
db_mach_reset_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif)
{
if (cpu_reset_address == NULL) {
db_printf("cpu_reset_address is not set\n");
return;
}
cpu_reset_address();
}
const struct db_command db_machine_command_table[] = {
#ifdef MULTIPROCESSOR
{ DDB_ADD_CMD("cpu", db_mach_cpu_cmd, 0,
"switch to another cpu", "cpu#", NULL) },
#endif
{ DDB_ADD_CMD("cp0", db_cp0dump_cmd, 0,
"Dump CP0 registers.",
NULL, NULL) },
{ 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") },
#ifdef MIPS64_XLS
{ DDB_ADD_CMD("mfcr", db_mfcr_cmd, CS_NOREPEAT,
"Dump processor control register",
NULL, NULL) },
{ DDB_ADD_CMD("mtcr", db_mtcr_cmd, CS_NOREPEAT|CS_MORE,
"Set processor control register",
NULL, NULL) },
#endif
#if defined(MIPS64_OCTEON) && defined(MULTIPROCESSOR)
{ DDB_ADD_CMD("nmi", db_mach_nmi_cmd, CS_NOREPEAT,
"Send NMI to processor",
"cpu#", NULL) },
#endif
{ 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) },
#if (MIPS32 + MIPS32R2 + MIPS64 + MIPS64R2) > 0
{ DDB_ADD_CMD("watch", db_watch_cmd, CS_MORE,
"set cp0 watchpoint",
"address <mask> <asid> </rwxma>", NULL) },
{ DDB_ADD_CMD("unwatch",db_unwatch_cmd, 0,
"delete cp0 watchpoint",
"address", NULL) },
#endif
{ DDB_END_CMD },
};
#endif
bool
inst_branch(int inst)
{
InstFmt i;
int delslt;
i.word = inst;
delslt = 0;
switch (i.JType.op) {
case OP_REGIMM:
case OP_J:
case OP_JAL:
#if MIPS64_OCTEON
case OP_CVM_BBIT0:
case OP_CVM_BBIT032:
case OP_CVM_BBIT1:
case OP_CVM_BBIT132:
#endif
case OP_BEQ:
case OP_BNE:
case OP_BLEZ:
case OP_BGTZ:
case OP_BEQL:
case OP_BNEL:
case OP_BLEZL:
case OP_BGTZL:
delslt = 1;
break;
case OP_COP0:
case OP_COP1:
switch (i.RType.rs) {
case OP_BCx:
case OP_BCy:
delslt = 1;
}
break;
case OP_SPECIAL:
if (i.RType.op == OP_JR || i.RType.op == OP_JALR)
delslt = 1;
break;
}
return delslt;
}
bool
inst_call(int inst)
{
bool call;
InstFmt i;
i.word = inst;
if (i.JType.op == OP_SPECIAL
&& ((i.RType.func == OP_JR && i.RType.rs != 31) ||
i.RType.func == OP_JALR))
call = 1;
else if (i.JType.op == OP_JAL)
call = 1;
else
call = 0;
return call;
}
bool
inst_return(int inst)
{
InstFmt i;
i.word = inst;
return (i.JType.op == OP_SPECIAL && i.RType.func == OP_JR &&
i.RType.rs == 31);
}
bool
inst_unconditional_flow_transfer(int inst)
{
InstFmt i;
bool jump;
i.word = inst;
jump = (i.JType.op == OP_J) ||
(i.JType.op == OP_SPECIAL && i.RType.func == OP_JR);
return jump;
}
bool
inst_load(int inst)
{
InstFmt i = { .word = inst, };
if ((i.JType.op & 050) != 040)
return false;
return i.JType.op != OP_PREF;
}
bool
inst_store(int inst)
{
InstFmt i = { .word = inst, };
if ((i.JType.op & 050) != 050)
return false;
return i.JType.op != OP_RSVD073 && i.JType.op != OP_CACHE;
}
db_addr_t
branch_taken(int inst, db_addr_t pc, db_regs_t *regs)
{
struct pcb * const pcb = lwp_getpcb(curlwp);
const uint32_t fpucsr = PCB_FSR(pcb);
vaddr_t ra;
ra = mips_emul_branch((struct trapframe *)regs, pc, fpucsr, false);
return ra;
}
db_addr_t
next_instr_address(db_addr_t pc, bool bd)
{
uint32_t ins;
if (bd == false)
return (pc + 4);
if (pc < MIPS_KSEG0_START)
ins = mips_ufetch32((void *)pc);
else
ins = *(uint32_t *)pc;
if (inst_branch(ins) || inst_call(ins) || inst_return(ins))
return (pc + 4);
return (pc);
}
#ifdef MULTIPROCESSOR
bool
ddb_running_on_this_cpu_p(void)
{
return ddb_cpu == cpu_number();
}
bool
ddb_running_on_any_cpu_p(void)
{
return ddb_cpu != NOCPU;
}
void
db_resume_others(void)
{
u_int cpu_me = cpu_number();
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_number(), cpu_index(ci));
db_continue_cmd(0, false, 0, "");
}
}
#endif
#endif