1 /* $NetBSD: db_machdep.c,v 1.39 2021/02/23 07:13:51 mrg Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Mark Brinicombe 5 * 6 * Mach Operating System 7 * Copyright (c) 1991,1990 Carnegie Mellon University 8 * All Rights Reserved. 9 * 10 * Permission to use, copy, modify and distribute this software and its 11 * documentation is hereby granted, provided that both the copyright 12 * notice and this permission notice appear in all copies of the 13 * software, derivative works or modified versions, and any portions 14 * thereof, and that both notices appear in supporting documentation. 15 * 16 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 17 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 18 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 19 * 20 * Carnegie Mellon requests users of this software to return to 21 * 22 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU 23 * School of Computer Science 24 * Carnegie Mellon University 25 * Pittsburgh PA 15213-3890 26 * 27 * any improvements or extensions that they make and grant Carnegie the 28 * rights to redistribute these changes. 29 */ 30 31 #ifdef _KERNEL_OPT 32 #include "opt_cputypes.h" 33 #include "opt_multiprocessor.h" 34 #endif 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: db_machdep.c,v 1.39 2021/02/23 07:13:51 mrg Exp $"); 38 39 #include <sys/param.h> 40 41 #include <sys/cpu.h> 42 #include <sys/proc.h> 43 #include <sys/vnode.h> 44 #include <sys/systm.h> 45 46 #include <arm/arm32/db_machdep.h> 47 #include <arm/arm32/machdep.h> 48 #include <arm/cpufunc.h> 49 50 #include <ddb/db_access.h> 51 #include <ddb/db_sym.h> 52 #include <ddb/db_output.h> 53 #include <ddb/db_variables.h> 54 #include <ddb/db_command.h> 55 #include <ddb/db_run.h> 56 57 #ifndef _KERNEL 58 #include <stddef.h> 59 #endif 60 61 #ifdef _KERNEL 62 static long nil; 63 64 void db_md_cpuinfo_cmd(db_expr_t, bool, db_expr_t, const char *); 65 66 int db_access_und_sp(const struct db_variable *, db_expr_t *, int); 67 int db_access_abt_sp(const struct db_variable *, db_expr_t *, int); 68 int db_access_irq_sp(const struct db_variable *, db_expr_t *, int); 69 #endif 70 71 static int 72 ddb_reg_var(const struct db_variable *v, db_expr_t *ep, int op) 73 { 74 register_t * const rp = (register_t *)DDB_REGS; 75 if (op == DB_VAR_SET) { 76 rp[(uintptr_t)v->valuep] = *ep; 77 } else { 78 *ep = rp[(uintptr_t)v->valuep]; 79 } 80 return 0; 81 } 82 83 84 #define XO(f) ((long *)(offsetof(db_regs_t, f) / sizeof(register_t))) 85 const struct db_variable db_regs[] = { 86 { "spsr", XO(tf_spsr), ddb_reg_var, NULL }, 87 { "r0", XO(tf_r0), ddb_reg_var, NULL }, 88 { "r1", XO(tf_r1), ddb_reg_var, NULL }, 89 { "r2", XO(tf_r2), ddb_reg_var, NULL }, 90 { "r3", XO(tf_r3), ddb_reg_var, NULL }, 91 { "r4", XO(tf_r4), ddb_reg_var, NULL }, 92 { "r5", XO(tf_r5), ddb_reg_var, NULL }, 93 { "r6", XO(tf_r6), ddb_reg_var, NULL }, 94 { "r7", XO(tf_r7), ddb_reg_var, NULL }, 95 { "r8", XO(tf_r8), ddb_reg_var, NULL }, 96 { "r9", XO(tf_r9), ddb_reg_var, NULL }, 97 { "r10", XO(tf_r10), ddb_reg_var, NULL }, 98 { "r11", XO(tf_r11), ddb_reg_var, NULL }, 99 { "r12", XO(tf_r12), ddb_reg_var, NULL }, 100 { "usr_sp", XO(tf_usr_sp), ddb_reg_var, NULL }, 101 { "usr_lr", XO(tf_usr_lr), ddb_reg_var, NULL }, 102 { "svc_sp", XO(tf_svc_sp), ddb_reg_var, NULL }, 103 { "svc_lr", XO(tf_svc_lr), ddb_reg_var, NULL }, 104 { "pc", XO(tf_pc), ddb_reg_var, NULL }, 105 #ifdef _KERNEL 106 { "und_sp", &nil, db_access_und_sp, NULL }, 107 { "abt_sp", &nil, db_access_abt_sp, NULL }, 108 { "irq_sp", &nil, db_access_irq_sp, NULL }, 109 #endif 110 }; 111 #undef XO 112 113 const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); 114 115 const struct db_command db_machine_command_table[] = { 116 #ifdef _KERNEL 117 #if defined(MULTIPROCESSOR) 118 { DDB_ADD_CMD("cpu", db_switch_cpu_cmd, 0, 119 "switch to a different cpu", 120 NULL,NULL) }, 121 #endif /* MULTIPROCESSOR */ 122 { DDB_ADD_CMD("cpuinfo", db_md_cpuinfo_cmd, 0, 123 "Displays the cpuinfo", 124 NULL, NULL) 125 }, 126 { DDB_ADD_CMD("fault", db_show_fault_cmd, 0, 127 "Displays the fault registers", 128 NULL,NULL) }, 129 #endif 130 { DDB_ADD_CMD("frame", db_show_frame_cmd, 0, 131 "Displays the contents of a trapframe", 132 "[address]", 133 " address:\taddress of trapfame to display")}, 134 #ifdef _KERNEL 135 { DDB_ADD_CMD("reset", db_reset_cmd, 0, 136 "Reset the system", 137 NULL,NULL) }, 138 #ifdef _ARM_ARCH_7 139 { DDB_ADD_CMD("tlb", db_show_tlb_cmd, 0, 140 "Displays the TLB", 141 NULL,NULL) }, 142 #endif 143 #endif /* _KERNEL */ 144 145 { DDB_END_CMD }, 146 }; 147 148 void 149 db_show_frame_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) 150 { 151 struct trapframe *frame; 152 153 if (!have_addr) { 154 db_printf("frame address must be specified\n"); 155 return; 156 } 157 158 frame = (struct trapframe *)addr; 159 160 db_printf("frame address = %08x ", (u_int)frame); 161 db_printf("spsr=%08x\n", frame->tf_spsr); 162 db_printf("r0 =%08x r1 =%08x r2 =%08x r3 =%08x\n", 163 frame->tf_r0, frame->tf_r1, frame->tf_r2, frame->tf_r3); 164 db_printf("r4 =%08x r5 =%08x r6 =%08x r7 =%08x\n", 165 frame->tf_r4, frame->tf_r5, frame->tf_r6, frame->tf_r7); 166 db_printf("r8 =%08x r9 =%08x r10=%08x r11=%08x\n", 167 frame->tf_r8, frame->tf_r9, frame->tf_r10, frame->tf_r11); 168 db_printf("r12=%08x r13=%08x r14=%08x r15=%08x\n", 169 frame->tf_r12, frame->tf_usr_sp, frame->tf_usr_lr, frame->tf_pc); 170 db_printf("slr=%08x ssp=%08x\n", frame->tf_svc_lr, frame->tf_svc_sp); 171 } 172 173 #ifdef _KERNEL 174 int 175 db_access_und_sp(const struct db_variable *vp, db_expr_t *valp, int rw) 176 { 177 178 if (rw == DB_VAR_GET) 179 *valp = get_stackptr(PSR_UND32_MODE); 180 return(0); 181 } 182 183 int 184 db_access_abt_sp(const struct db_variable *vp, db_expr_t *valp, int rw) 185 { 186 187 if (rw == DB_VAR_GET) 188 *valp = get_stackptr(PSR_ABT32_MODE); 189 return(0); 190 } 191 192 int 193 db_access_irq_sp(const struct db_variable *vp, db_expr_t *valp, int rw) 194 { 195 196 if (rw == DB_VAR_GET) 197 *valp = get_stackptr(PSR_IRQ32_MODE); 198 return(0); 199 } 200 201 void 202 db_show_fault_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) 203 { 204 db_printf("DFAR=%#x DFSR=%#x IFAR=%#x IFSR=%#x\n", 205 armreg_dfar_read(), armreg_dfsr_read(), 206 armreg_ifar_read(), armreg_ifsr_read()); 207 db_printf("CONTEXTIDR=%#x TTBCR=%#x TTBR=%#x\n", 208 armreg_contextidr_read(), armreg_ttbcr_read(), 209 armreg_ttbr_read()); 210 } 211 212 void 213 db_reset_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) 214 { 215 if (cpu_reset_address == NULL) { 216 db_printf("cpu_reset_address is not set\n"); 217 return; 218 } 219 220 cpu_reset_address(); 221 } 222 223 #ifdef _ARM_ARCH_7 224 static void 225 tlb_print_common_header(const char *str) 226 { 227 db_printf("-W/I-- ----VA---- ----PA---- --SIZE-- D AP XN ASD %s\n", str); 228 } 229 230 static void 231 tlb_print_addr(size_t way, size_t va_index, vaddr_t vpn, paddr_t pfn) 232 { 233 db_printf("[%1zu:%02zx] 0x%05lx000 0x%05lx000", way, va_index, vpn, pfn); 234 } 235 236 static void 237 tlb_print_size_domain_prot(const char *sizestr, u_int domain, u_int ap, 238 bool xn_p) 239 { 240 db_printf(" %8s %1x %2d %s", sizestr, domain, ap, (xn_p ? "XN" : "--")); 241 } 242 243 static void 244 tlb_print_asid(bool ng_p, tlb_asid_t asid) 245 { 246 if (ng_p) { 247 db_printf(" %3d", asid); 248 } else { 249 db_printf(" ---"); 250 } 251 } 252 253 struct db_tlbinfo { 254 vaddr_t (*dti_decode_vpn)(size_t, uint32_t, uint32_t); 255 void (*dti_print_header)(void); 256 void (*dti_print_entry)(size_t, size_t, uint32_t, uint32_t); 257 u_int dti_index; 258 }; 259 260 static void 261 tlb_print_cortex_a5_header(void) 262 { 263 tlb_print_common_header(" S TEX C B"); 264 } 265 266 static vaddr_t 267 tlb_decode_cortex_a5_vpn(size_t va_index, uint32_t d0, uint32_t d1) 268 { 269 const uint64_t d = ((uint64_t)d1 << 32) | d0; 270 271 const u_int size = __SHIFTOUT(d, ARM_A5_TLBDATA_SIZE); 272 return __SHIFTOUT(d, ARM_A5_TLBDATA_VA) * (ARM_A5_TLBDATAOP_INDEX + 1) 273 + (va_index << (4*size)); 274 } 275 276 static void 277 tlb_print_cortex_a5_entry(size_t way, size_t va_index, uint32_t d0, uint32_t d1) 278 { 279 static const char size_strings[4][8] = { 280 " 4KB ", " 64KB ", " 1MB ", " 16MB ", 281 }; 282 283 const uint64_t d = ((uint64_t)d1 << 32) | d0; 284 285 const paddr_t pfn = __SHIFTOUT(d, ARM_A5_TLBDATA_PA); 286 const vaddr_t vpn = tlb_decode_cortex_a5_vpn(va_index, d0, d1); 287 288 tlb_print_addr(way, va_index, vpn, pfn); 289 290 const u_int size = __SHIFTOUT(d, ARM_A5_TLBDATA_SIZE); 291 const u_int domain = __SHIFTOUT(d, ARM_A5_TLBDATA_DOM); 292 const u_int ap = __SHIFTOUT(d, ARM_A5_TLBDATA_AP); 293 const bool xn_p = (d & ARM_A5_TLBDATA_XN) != 0; 294 295 tlb_print_size_domain_prot(size_strings[size], domain, ap, xn_p); 296 297 const bool ng_p = (d & ARM_A5_TLBDATA_nG) != 0; 298 const tlb_asid_t asid = __SHIFTOUT(d, ARM_A5_TLBDATA_ASID); 299 300 tlb_print_asid(ng_p, asid); 301 302 const u_int tex = __SHIFTOUT(d, ARM_A5_TLBDATA_TEX); 303 const bool c_p = (d & ARM_A5_TLBDATA_C) != 0; 304 const bool b_p = (d & ARM_A5_TLBDATA_B) != 0; 305 const bool s_p = (d & ARM_A5_TLBDATA_S) != 0; 306 307 db_printf(" %c %d %c %c\n", (s_p ? 'S' : '-'), tex, 308 (c_p ? 'C' : '-'), (b_p ? 'B' : '-')); 309 } 310 311 static const struct db_tlbinfo tlb_cortex_a5_info = { 312 .dti_decode_vpn = tlb_decode_cortex_a5_vpn, 313 .dti_print_header = tlb_print_cortex_a5_header, 314 .dti_print_entry = tlb_print_cortex_a5_entry, 315 .dti_index = ARM_A5_TLBDATAOP_INDEX, 316 }; 317 318 static const char tlb_cortex_a7_esizes[8][8] = { 319 " 4KB(S)", " 4KB(L)", "64KB(S)", "64KB(L)", 320 " 1MB(S)", " 2MB(L)", "16MB(S)", " 1GB(L)", 321 }; 322 323 static void 324 tlb_print_cortex_a7_header(void) 325 { 326 tlb_print_common_header("IS --OS- SH"); 327 } 328 329 static inline vaddr_t 330 tlb_decode_cortex_a7_vpn(size_t va_index, uint32_t d0, uint32_t d1) 331 { 332 const u_int size = __SHIFTOUT(d0, ARM_A7_TLBDATA0_SIZE); 333 const u_int shift = (size & 1) 334 ? ((0x12090400 >> (8*size)) & 0x1f) 335 : (2 * size); 336 337 return __SHIFTOUT(d0, ARM_A7_TLBDATA0_VA) * (ARM_A7_TLBDATAOP_INDEX + 1) 338 + (va_index << shift); 339 } 340 341 static void 342 tlb_print_cortex_a7_entry(size_t way, size_t va_index, uint32_t d0, uint32_t d1) 343 { 344 const uint32_t d2 = armreg_tlbdata2_read(); 345 const uint64_t d01 = ((uint64_t)d1 << 32) | d0; 346 const uint64_t d12 = ((uint64_t)d2 << 32) | d1; 347 348 const paddr_t pfn = __SHIFTOUT(d12, ARM_A7_TLBDATA12_PA); 349 const vaddr_t vpn = tlb_decode_cortex_a7_vpn(va_index, d0, d1); 350 351 tlb_print_addr(way, va_index, vpn, pfn); 352 353 const u_int size = __SHIFTOUT(d0, ARM_A7_TLBDATA0_SIZE); 354 const u_int domain = __SHIFTOUT(d2, ARM_A7_TLBDATA2_DOM); 355 const u_int ap = __SHIFTOUT(d1, ARM_A7_TLBDATA1_AP); 356 const bool xn_p = (d2 & ARM_A7_TLBDATA2_XN1) != 0; 357 358 tlb_print_size_domain_prot(tlb_cortex_a7_esizes[size], domain, ap, xn_p); 359 360 const bool ng_p = (d1 & ARM_A7_TLBDATA1_nG) != 0; 361 const tlb_asid_t asid = __SHIFTOUT(d01, ARM_A7_TLBDATA01_ASID); 362 363 tlb_print_asid(ng_p, asid); 364 365 const u_int is = __SHIFTOUT(d2, ARM_A7_TLBDATA2_IS); 366 if (is == ARM_A7_TLBDATA2_IS_DSO) { 367 u_int mt = __SHIFTOUT(d2, ARM_A7_TLBDATA2_SDO_MT); 368 switch (mt) { 369 case ARM_A7_TLBDATA2_SDO_MT_D: 370 db_printf(" DV\n"); 371 return; 372 case ARM_A7_TLBDATA2_SDO_MT_SO: 373 db_printf(" SO\n"); 374 return; 375 default: 376 db_printf(" %02u\n", mt); 377 return; 378 } 379 } 380 const u_int os = __SHIFTOUT(d2, ARM_A7_TLBDATA2_OS); 381 const u_int sh = __SHIFTOUT(d2, ARM_A7_TLBDATA2_SH); 382 static const char is_types[3][3] = { "NC", "WB", "WT" }; 383 static const char os_types[4][6] = { "NC", "WB+WA", "WT", "WB" }; 384 static const char sh_types[4][3] = { "NS", "na", "OS", "IS" }; 385 db_printf(" %2s %5s %2s\n", is_types[is], os_types[os], sh_types[sh]); 386 } 387 388 static const struct db_tlbinfo tlb_cortex_a7_info = { 389 .dti_decode_vpn = tlb_decode_cortex_a7_vpn, 390 .dti_print_header = tlb_print_cortex_a7_header, 391 .dti_print_entry = tlb_print_cortex_a7_entry, 392 .dti_index = ARM_A7_TLBDATAOP_INDEX, 393 }; 394 395 static inline const struct db_tlbinfo * 396 tlb_lookup_tlbinfo(void) 397 { 398 const bool cortex_a5_p = CPU_ID_CORTEX_A5_P(curcpu()->ci_arm_cpuid); 399 const bool cortex_a7_p = CPU_ID_CORTEX_A7_P(curcpu()->ci_arm_cpuid); 400 if (cortex_a5_p) { 401 return &tlb_cortex_a5_info; 402 } 403 if (cortex_a7_p) { 404 return &tlb_cortex_a7_info; 405 } 406 return NULL; 407 } 408 409 void 410 db_show_tlb_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) 411 { 412 const struct db_tlbinfo * const dti = tlb_lookup_tlbinfo(); 413 414 if (dti == NULL) { 415 db_printf("not supported on this CPU\n"); 416 return; 417 } 418 419 if (have_addr) { 420 const vaddr_t vpn = (vaddr_t)addr >> L2_S_SHIFT; 421 const u_int va_index = vpn & dti->dti_index; 422 for (size_t way = 0; way < 2; way++) { 423 armreg_tlbdataop_write( 424 __SHIFTIN(va_index, dti->dti_index) 425 | __SHIFTIN(way, ARM_TLBDATAOP_WAY)); 426 isb(); 427 const uint32_t d0 = armreg_tlbdata0_read(); 428 const uint32_t d1 = armreg_tlbdata1_read(); 429 if ((d0 & ARM_TLBDATA_VALID) 430 && vpn == (*dti->dti_decode_vpn)(va_index, d0, d1)) { 431 (*dti->dti_print_header)(); 432 (*dti->dti_print_entry)(way, va_index, d0, d1); 433 return; 434 } 435 } 436 db_printf("VA %#"DDB_EXPR_FMT"x not found in TLB\n", addr); 437 return; 438 } 439 440 bool first = true; 441 size_t n = 0; 442 for (size_t va_index = 0; va_index <= dti->dti_index; va_index++) { 443 for (size_t way = 0; way < 2; way++) { 444 armreg_tlbdataop_write( 445 __SHIFTIN(way, ARM_TLBDATAOP_WAY) 446 | __SHIFTIN(va_index, dti->dti_index)); 447 isb(); 448 const uint32_t d0 = armreg_tlbdata0_read(); 449 const uint32_t d1 = armreg_tlbdata1_read(); 450 if (d0 & ARM_TLBDATA_VALID) { 451 if (first) { 452 (*dti->dti_print_header)(); 453 first = false; 454 } 455 (*dti->dti_print_entry)(way, va_index, d0, d1); 456 n++; 457 } 458 } 459 } 460 db_printf("%zu TLB valid entries found\n", n); 461 } 462 #endif 463 464 #if defined(MULTIPROCESSOR) 465 void 466 db_switch_cpu_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif) 467 { 468 if (addr >= maxcpus) { 469 db_printf("cpu %"DDB_EXPR_FMT"d out of range", addr); 470 return; 471 } 472 struct cpu_info *new_ci = cpu_lookup(addr); 473 if (new_ci == NULL) { 474 db_printf("cpu %"DDB_EXPR_FMT"d does not exist", addr); 475 return; 476 } 477 if (DDB_REGS->tf_spsr & PSR_T_bit) { 478 DDB_REGS->tf_pc -= 2; /* XXX */ 479 } else { 480 DDB_REGS->tf_pc -= 4; 481 } 482 db_newcpu = new_ci; 483 db_continue_cmd(0, false, 0, ""); 484 } 485 #endif 486 487 static void 488 show_cpuinfo(struct cpu_info *kci) 489 { 490 struct cpu_info cpuinfobuf; 491 cpuid_t cpuid; 492 int i; 493 494 db_read_bytes((db_addr_t)kci, sizeof(cpuinfobuf), (char *)&cpuinfobuf); 495 496 struct cpu_info *ci = &cpuinfobuf; 497 cpuid = ci->ci_cpuid; 498 db_printf("cpu_info=%p, cpu_name=%s\n", kci, ci->ci_cpuname); 499 db_printf("%p cpu[%lu].ci_cpuid = %lu\n", 500 &ci->ci_cpuid, cpuid, ci->ci_cpuid); 501 db_printf("%p cpu[%lu].ci_curlwp = %p\n", 502 &ci->ci_curlwp, cpuid, ci->ci_curlwp); 503 for (i = 0; i < SOFTINT_COUNT; i++) { 504 db_printf("%p cpu[%lu].ci_softlwps[%d] = %p\n", 505 &ci->ci_softlwps[i], cpuid, i, ci->ci_softlwps[i]); 506 } 507 db_printf("%p cpu[%lu].ci_lastintr = %" PRIu64 "\n", 508 &ci->ci_lastintr, cpuid, ci->ci_lastintr); 509 db_printf("%p cpu[%lu].ci_want_resched = %d\n", 510 &ci->ci_want_resched, cpuid, ci->ci_want_resched); 511 db_printf("%p cpu[%lu].ci_cpl = %d\n", 512 &ci->ci_cpl, cpuid, ci->ci_cpl); 513 db_printf("%p cpu[%lu].ci_softints = 0x%08x\n", 514 &ci->ci_softints, cpuid, ci->ci_softints); 515 db_printf("%p cpu[%lu].ci_intr_depth = %u\n", 516 &ci->ci_intr_depth, cpuid, ci->ci_intr_depth); 517 518 } 519 520 void 521 db_md_cpuinfo_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 522 const char *modif) 523 { 524 #ifdef MULTIPROCESSOR 525 CPU_INFO_ITERATOR cii; 526 struct cpu_info *ci; 527 bool showall = false; 528 529 if (modif != NULL) { 530 for (; *modif != '\0'; modif++) { 531 switch (*modif) { 532 case 'a': 533 showall = true; 534 break; 535 } 536 } 537 } 538 539 if (showall) { 540 for (CPU_INFO_FOREACH(cii, ci)) { 541 show_cpuinfo(ci); 542 } 543 } else 544 #endif /* MULTIPROCESSOR */ 545 show_cpuinfo(curcpu()); 546 } 547 548 #endif /* _KERNEL */ 549