root/sys/arch/mips/mips/db_interface.c
/*      $NetBSD: db_interface.c,v 1.101 2026/02/25 05:34:42 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.101 2026/02/25 05:34:42 skrll Exp $");

#ifdef _KERNEL_OPT
#include "opt_multiprocessor.h"
#include "opt_cputype.h"        /* which mips CPUs do we support? */
#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> /* for db_continue_cmd() proto */
#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  /* (MIPS32 + MIPS32R2 + MIPS64 + MIPS64R2) > 0 */

#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:           /* watchpoint */
        case T_BREAK:           /* breakpoint */
                printf("kernel: %s trap\n", trap_names[type & 0x1f]);
                break;
        case -1:                /* keyboard interrupt */
                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");
                        /*NOTREACHED*/
                }
                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 /*code*/);
        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  /* !KGDB */

/*
 * Read bytes from kernel address space for debugger.
 */
void
db_read_bytes(vaddr_t addr, size_t size, char *data)
{
        const char *src = (char *)addr;
        int err;

        /* If asked to fetch from userspace, do it safely */
        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++;
}

/*
 * Write bytes to kernel address space for debugger.
 */
void
db_write_bytes(vaddr_t addr, size_t size, const char *data)
{
        char *p = (char *)addr;
        size_t n = size;
        int err;

        /* If asked to store to userspace, do it safely */
        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;       /* skip invalid TLBs */
                        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;       /* skip invalid TLBs */

                        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) {
                /*
                 * Cast the physical address -- some platforms, while
                 * being ILP32, may be using 64-bit paddr_t's.
                 */
                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)

/* XXX not 64-bit ABI safe! */
#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)

/* XXX not 64-bit ABI safe! */
#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 /* MULTIPROCESSOR */
                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;       /* empty/disabled/invalid */
                        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;
        }

        /*
         * parse modif to define mode
         */
        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 mask mode is requested get the mask,
         */
        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 asid mode is requested, get the asid;
         * otherwise use global mode (and set asid=0)
         */
        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();

        /*
         * store to the (volatile) table entry
         * other CPUs can see this and load when resuming from pause
         */
        cwp->cw_addr = (register_t)address;
        cwp->cw_mask = (register_t)mask;
        cwp->cw_asid = asid;
        cwp->cw_mode = mode;

        /*
         * program the CPU watchpoint regs
         */
        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  /* (MIPS32 + MIPS32R2 + MIPS64 + MIPS64R2) > 0 */

#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;
        }

        /* value = CR[addr] */
        __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();

        /* CR[addr] = value */
        __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 /* MIPS64_XLS */

#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  /* OCTEON + MP */
        { 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  /* (MIPS32 + MIPS32R2 + MIPS64 + MIPS64R2) > 0 */
        { DDB_END_CMD },
};
#endif  /* !KGDB */

/*
 * Determine whether the instruction involves a delay slot.
 */
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;
}

/*
 * Determine whether the instruction calls a function.
 */
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;
}

/*
 * Determine whether the instruction returns from a function (j ra).  The
 * compiler can use this construct for other jumps, but usually will not.
 * This lets the ddb "next" command to work (also need inst_trap_return()).
 */
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);
}

/*
 * Determine whether the instruction makes a jump.
 */
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;
}

/*
 * Determine whether the instruction is a load/store as appropriate.
 */
bool
inst_load(int inst)
{
        InstFmt i = { .word = inst, };

        /*
         * All loads are opcodes 04x or 06x.
         */
        if ((i.JType.op & 050) != 040)
                return false;

        /*
         * Except these this opcode is not a load.
         */
        return i.JType.op != OP_PREF;
}

bool
inst_store(int inst)
{
        InstFmt i = { .word = inst, };

        /*
         * All stores are opcodes 05x or 07x.
         */
        if ((i.JType.op & 050) != 050)
                return false;

        /*
         * Except these two opcodes are not stores.
         */
        return i.JType.op != OP_RSVD073 && i.JType.op != OP_CACHE;
}

/*
 * Return the next pc if the given branch is taken.
 * mips_emul_branch() runs analysis for branch delay slot.
 */
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;
}

/*
 * Return the next pc of an arbitrary instruction.
 */
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  /* MULTIPROCESSOR */

#endif  /* _KERNEL */