/* $NetBSD: db_interface.c,v 1.99 2023/02/19 11:19:51 simonb 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 __KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.99 2023/02/19 11:19:51 simonb 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef KGDB #include #include #include #include #include #include #include /* 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; #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(1); db_trap(type & ~T_USER, 0 /*code*/); cnpollc(0); 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 #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 ", 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 */