1 /* $NetBSD: db_interface.c,v 1.25 2026/05/30 06:30:14 skrll Exp $ */ 2 3 /* 4 * Copyright (c) 2017 Ryo Shimizu 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "opt_cpuoptions.h" 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.25 2026/05/30 06:30:14 skrll Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/types.h> 36 37 #include <uvm/uvm.h> 38 #include <uvm/uvm_ddb.h> 39 #include <uvm/uvm_prot.h> 40 #ifdef __HAVE_PMAP_PV_TRACK 41 #include <uvm/pmap/pmap_pvt.h> 42 #endif 43 44 #include <aarch64/armreg.h> 45 #include <aarch64/db_machdep.h> 46 #include <aarch64/locore.h> 47 #include <aarch64/machdep.h> 48 #include <aarch64/pmap.h> 49 50 #include <arm/cpufunc.h> 51 52 #include <ddb/db_access.h> 53 #include <ddb/db_command.h> 54 #include <ddb/db_output.h> 55 #include <ddb/db_variables.h> 56 #include <ddb/db_sym.h> 57 #include <ddb/db_extern.h> 58 #include <ddb/db_interface.h> 59 60 #include <dev/cons.h> 61 62 db_regs_t ddb_regs; 63 64 static bool 65 db_accessible_address(vaddr_t addr, bool readonly) 66 { 67 register_t s; 68 uint64_t par; 69 int space; 70 71 space = aarch64_addressspace(addr); 72 if (space != AARCH64_ADDRSPACE_LOWER && 73 space != AARCH64_ADDRSPACE_UPPER) 74 return false; 75 76 s = daif_disable(DAIF_I|DAIF_F); 77 78 switch (aarch64_addressspace(addr)) { 79 case AARCH64_ADDRSPACE_LOWER: 80 if (readonly) 81 reg_s1e0r_write(addr); 82 else 83 reg_s1e0w_write(addr); 84 break; 85 case AARCH64_ADDRSPACE_UPPER: 86 if (readonly) 87 reg_s1e1r_write(addr); 88 else 89 reg_s1e1w_write(addr); 90 break; 91 } 92 isb(); 93 par = reg_par_el1_read(); 94 95 reg_daif_write(s); 96 97 return ((par & PAR_F) == 0); 98 } 99 100 void 101 db_read_bytes(vaddr_t addr, size_t size, char *data) 102 { 103 vaddr_t lastpage = -1; 104 const char *src; 105 106 for (src = (const char *)addr; size > 0;) { 107 const vaddr_t va = (vaddr_t)src; 108 uintptr_t tmp; 109 110 if (lastpage != atop(va) && !db_accessible_address(va, true)) { 111 db_printf("address %p is invalid\n", src); 112 memset(data, 0, size); /* stubs are filled by zero */ 113 return; 114 } 115 lastpage = atop(va); 116 117 if (aarch64_pan_enabled) 118 reg_pan_write(0); /* disable PAN */ 119 120 tmp = (uintptr_t)src | (uintptr_t)data; 121 if (size >= 8 && (tmp & 7) == 0) { 122 *(uint64_t *)data = *(const uint64_t *)src; 123 src += 8; 124 data += 8; 125 size -= 8; 126 } else if (size >= 4 && (tmp & 3) == 0) { 127 *(uint32_t *)data = *(const uint32_t *)src; 128 src += 4; 129 data += 4; 130 size -= 4; 131 } else if (size >= 2 && (tmp & 1) == 0) { 132 *(uint16_t *)data = *(const uint16_t *)src; 133 src += 2; 134 data += 2; 135 size -= 2; 136 } else { 137 *data++ = *src++; 138 size--; 139 } 140 141 if (aarch64_pan_enabled) 142 reg_pan_write(1); /* enable PAN */ 143 } 144 } 145 146 static void 147 db_write_text(vaddr_t addr, size_t size, const char *data) 148 { 149 pt_entry_t *ptep, pte; 150 size_t s; 151 152 /* 153 * consider page boundary, and 154 * it works even if kernel_text is mapped with L2 or L3. 155 */ 156 if (atop(addr) != atop(addr + size - 1)) { 157 s = PAGE_SIZE - (addr & PAGE_MASK); 158 db_write_text(addr, s, data); 159 addr += s; 160 size -= s; 161 data += s; 162 } 163 while (size > 0) { 164 ptep = kvtopte(addr); 165 KASSERT(ptep != NULL); 166 167 /* 168 * change to writable. it is required to keep execute permission. 169 * because if the block/page to which the target address belongs is 170 * the same as the block/page to which this function belongs, then 171 * if PROT_EXECUTE is dropped and TLB is invalidated, the program 172 * will stop... 173 */ 174 /* old pte is returned by pmap_kvattr */ 175 pte = pmap_kvattr(ptep, VM_PROT_EXECUTE | VM_PROT_READ | VM_PROT_WRITE); 176 /* dsb(ishst) included in aarch64_tlbi_by_va */ 177 aarch64_tlbi_by_va(addr); 178 179 s = size; 180 if (size > PAGE_SIZE) 181 s = PAGE_SIZE; 182 183 memcpy((void *)addr, data, s); 184 cpu_icache_sync_range(addr, size); 185 186 /* restore pte */ 187 *ptep = pte; 188 /* dsb(ishst) included in aarch64_tlbi_by_va */ 189 aarch64_tlbi_by_va(addr); 190 191 addr += s; 192 size -= s; 193 data += s; 194 } 195 } 196 197 void 198 db_write_bytes(vaddr_t addr, size_t size, const char *data) 199 { 200 vaddr_t kernstart, datastart; 201 vaddr_t lastpage = -1; 202 char *dst; 203 204 /* if readonly page, require changing attribute to write */ 205 extern char __kernel_text[], __data_start[]; 206 kernstart = trunc_page((vaddr_t)__kernel_text); 207 datastart = trunc_page((vaddr_t)__data_start); 208 if (kernstart <= addr && addr < datastart) { 209 size_t s; 210 211 s = datastart - addr; 212 if (s > size) 213 s = size; 214 db_write_text(addr, s, data); 215 addr += s; 216 size -= s; 217 data += s; 218 } 219 220 for (dst = (char *)addr; size > 0;) { 221 const vaddr_t va = (vaddr_t)dst; 222 uintptr_t tmp; 223 224 if (lastpage != atop(va) && !db_accessible_address(va, false)) { 225 db_printf("address %p is invalid\n", dst); 226 return; 227 } 228 lastpage = atop(va); 229 230 if (aarch64_pan_enabled) 231 reg_pan_write(0); /* disable PAN */ 232 233 tmp = (uintptr_t)dst | (uintptr_t)data; 234 if (size >= 8 && (tmp & 7) == 0) { 235 *(uint64_t *)dst = *(const uint64_t *)data; 236 dst += 8; 237 data += 8; 238 size -= 8; 239 } else if (size >= 4 && (tmp & 3) == 0) { 240 *(uint32_t *)dst = *(const uint32_t *)data; 241 dst += 4; 242 data += 4; 243 size -= 4; 244 } else if (size >= 2 && (tmp & 1) == 0) { 245 *(uint16_t *)dst = *(const uint16_t *)data; 246 dst += 2; 247 data += 2; 248 size -= 2; 249 } else { 250 *dst++ = *data++; 251 size--; 252 } 253 254 if (aarch64_pan_enabled) 255 reg_pan_write(1); /* enable PAN */ 256 } 257 } 258 259 /* 260 * return register value of $X0..$X30, $SP or 0($XZR) 261 */ 262 static uint64_t 263 db_fetch_reg(unsigned int reg, db_regs_t *regs, bool use_sp) 264 { 265 if (reg >= 32) 266 panic("db_fetch_reg: botch"); 267 268 if (reg == 31) { 269 /* $SP or $XZR */ 270 return use_sp ? regs->tf_sp : 0; 271 } 272 return regs->tf_reg[reg]; 273 } 274 275 static inline uint64_t 276 SignExtend(int bitwidth, uint64_t imm, unsigned int multiply) 277 { 278 const uint64_t signbit = ((uint64_t)1 << (bitwidth - 1)); 279 const uint64_t immmax = signbit << 1; 280 281 if (imm & signbit) 282 imm -= immmax; 283 return imm * multiply; 284 } 285 286 db_addr_t 287 db_branch_taken(db_expr_t inst, db_addr_t pc, db_regs_t *regs) 288 { 289 LE32TOH(inst); 290 291 #define INSN_FMT_RN(insn) (((insn) >> 5) & 0x1f) 292 #define INSN_FMT_IMM26(insn) ((insn) & 0x03ffffff) 293 #define INSN_FMT_IMM19(insn) (((insn) >> 5) & 0x7ffff) 294 #define INSN_FMT_IMM14(insn) (((insn) >> 5) & 0x3fff) 295 296 if ((inst & 0xfffffc1f) == 0xd65f0000 || /* ret xN */ 297 (inst & 0xfffffc1f) == 0xd63f0000 || /* blr xN */ 298 (inst & 0xfffffc1f) == 0xd61f0000) { /* br xN */ 299 return db_fetch_reg(INSN_FMT_RN(inst), regs, false); 300 } 301 302 if ((inst & 0xfc000000) == 0x94000000 || /* bl imm */ 303 (inst & 0xfc000000) == 0x14000000) { /* b imm */ 304 return SignExtend(26, INSN_FMT_IMM26(inst), 4) + pc; 305 } 306 307 if ((inst & 0xff000010) == 0x54000000 || /* b.cond */ 308 (inst & 0x7f000000) == 0x35000000 || /* cbnz */ 309 (inst & 0x7f000000) == 0x34000000) { /* cbz */ 310 return SignExtend(19, INSN_FMT_IMM19(inst), 4) + pc; 311 } 312 313 if ((inst & 0x7f000000) == 0x37000000 || /* tbnz */ 314 (inst & 0x7f000000) == 0x36000000) { /* tbz */ 315 return SignExtend(14, INSN_FMT_IMM14(inst), 4) + pc; 316 } 317 318 panic("branch_taken: botch"); 319 } 320 321 bool 322 db_inst_unconditional_flow_transfer(db_expr_t inst) 323 { 324 LE32TOH(inst); 325 326 if ((inst & 0xfffffc1f) == 0xd65f0000 || /* ret xN */ 327 (inst & 0xfc000000) == 0x94000000 || /* bl */ 328 (inst & 0xfffffc1f) == 0xd63f0000 || /* blr */ 329 (inst & 0xfc000000) == 0x14000000 || /* b imm */ 330 (inst & 0xfffffc1f) == 0xd61f0000) /* br */ 331 return true; 332 333 #define INSN_FMT_COND(insn) ((insn) & 0xf) 334 #define CONDITION_AL 14 335 336 if ((inst & 0xff000010) == 0x54000000 && /* b.cond */ 337 INSN_FMT_COND(inst) == CONDITION_AL) /* always? */ 338 return true; 339 340 return false; 341 } 342 343 void 344 db_pte_print(pt_entry_t pte, int level, 345 void (*pr)(const char *, ...) __printflike(1, 2)) 346 { 347 if (pte == 0) { 348 pr(" UNUSED\n"); 349 return; 350 } 351 352 pr(" %s", (pte & LX_VALID) ? "VALID" : "**INVALID**"); 353 354 if (level == 0 || 355 (level == 1 && l1pde_is_table(pte)) || 356 (level == 2 && l2pde_is_table(pte))) { 357 358 /* L0/L1/L2 TABLE */ 359 if (level == 0 && (pte & LX_TYPE) != LX_TYPE_TBL) 360 pr(" **ILLEGAL TYPE**"); /* L0 doesn't support block */ 361 else 362 pr(" L%d-TABLE", level); 363 364 pr(", PA=%lx", l0pde_pa(pte)); 365 366 if (pte & LX_TBL_NSTABLE) 367 pr(", NSTABLE"); 368 if (pte & LX_TBL_APTABLE) 369 pr(", APTABLE"); 370 if (pte & LX_TBL_UXNTABLE) 371 pr(", UXNTABLE"); 372 if (pte & LX_TBL_PXNTABLE) 373 pr(", PXNTABLE"); 374 375 } else if ((level == 1 && l1pde_is_block(pte)) || 376 (level == 2 && l2pde_is_block(pte)) || 377 level == 3) { 378 379 /* L1/L2 BLOCK or L3 PAGE */ 380 switch (level) { 381 case 1: 382 pr(" L1(1G)-BLOCK"); 383 break; 384 case 2: 385 pr(" L2(2M)-BLOCK"); 386 break; 387 case 3: 388 pr(" %s", l3pte_is_page(pte) ? 389 "L3(4K)-PAGE" : "**ILLEGAL TYPE**"); 390 break; 391 } 392 393 pr(", PA=%lx", l3pte_pa(pte)); 394 395 #ifdef ARMV81_HAFDBS 396 if (pte & LX_BLKPAG_DBM) 397 pr(", %s", "DBM"); 398 #endif 399 pr(", %s", (pte & LX_BLKPAG_UXN) ? "UXN" : "UX"); 400 pr(", %s", (pte & LX_BLKPAG_PXN) ? "PXN" : "PX"); 401 402 if (pte & LX_BLKPAG_CONTIG) 403 pr(", CONTIG"); 404 405 pr(", %s", (pte & LX_BLKPAG_NG) ? "nG" : "G"); 406 pr(", %s", (pte & LX_BLKPAG_AF) ? 407 "accessible" : 408 "**fault** "); 409 410 switch (pte & LX_BLKPAG_SH) { 411 case LX_BLKPAG_SH_NS: 412 pr(", SH_NS"); 413 break; 414 case LX_BLKPAG_SH_OS: 415 pr(", SH_OS"); 416 break; 417 case LX_BLKPAG_SH_IS: 418 pr(", SH_IS"); 419 break; 420 default: 421 pr(", SH_??"); 422 break; 423 } 424 425 pr(", %s", (pte & LX_BLKPAG_AP_RO) ? "RO" : "RW"); 426 pr(", %s", (pte & LX_BLKPAG_APUSER) ? "EL0" : "EL1"); 427 pr(", %s", (pte & LX_BLKPAG_NS) ? "NS" : "secure"); 428 429 switch (pte & LX_BLKPAG_ATTR_MASK) { 430 case LX_BLKPAG_ATTR_NORMAL_WB: 431 pr(", WB"); 432 break; 433 case LX_BLKPAG_ATTR_NORMAL_NC: 434 pr(", NC"); 435 break; 436 case LX_BLKPAG_ATTR_NORMAL_WT: 437 pr(", WT"); 438 break; 439 case LX_BLKPAG_ATTR_DEVICE_MEM: 440 pr(", DEV"); 441 break; 442 case LX_BLKPAG_ATTR_DEVICE_MEM_NP: 443 pr(", DEV(NP)"); 444 break; 445 default: 446 pr(", ATTR(%lu)", __SHIFTOUT(pte, LX_BLKPAG_ATTR_INDX)); 447 break; 448 } 449 450 if (pte & LX_BLKPAG_OS_0) 451 pr(", " PMAP_PTE_OS0); 452 if (pte & LX_BLKPAG_OS_1) 453 pr(", " PMAP_PTE_OS1); 454 if (pte & LX_BLKPAG_OS_2) 455 pr(", " PMAP_PTE_OS2); 456 if (pte & LX_BLKPAG_OS_3) 457 pr(", " PMAP_PTE_OS3); 458 } else { 459 pr(" **ILLEGAL TYPE**"); 460 } 461 pr("\n"); 462 } 463 464 void 465 db_pteinfo(vaddr_t va, void (*pr)(const char *, ...) __printflike(1, 2)) 466 { 467 struct vm_page *pg; 468 bool user; 469 pd_entry_t *l0, *l1, *l2, *l3; 470 pd_entry_t pde; 471 pt_entry_t pte; 472 uint64_t ttbr; 473 paddr_t pa; 474 unsigned int idx; 475 476 switch (aarch64_addressspace(va)) { 477 case AARCH64_ADDRSPACE_UPPER: 478 user = false; 479 ttbr = reg_ttbr1_el1_read(); 480 break; 481 case AARCH64_ADDRSPACE_LOWER: 482 user = true; 483 ttbr = reg_ttbr0_el1_read(); 484 break; 485 default: 486 pr("illegal address space\n"); 487 return; 488 } 489 pa = ttbr & TTBR_BADDR; 490 l0 = (pd_entry_t *)AARCH64_PA_TO_KVA(pa); 491 492 /* 493 * traverse L0 -> L1 -> L2 -> L3 table 494 */ 495 pr("TTBR%d=%016"PRIx64", pa=%016"PRIxPADDR", va=%p", 496 user ? 0 : 1, ttbr, pa, l0); 497 pr(", input-va=%016"PRIxVADDR 498 ", L0-index=%ld, L1-index=%ld, L2-index=%ld, L3-index=%ld\n", 499 va, 500 (va & L0_ADDR_BITS) >> L0_SHIFT, 501 (va & L1_ADDR_BITS) >> L1_SHIFT, 502 (va & L2_ADDR_BITS) >> L2_SHIFT, 503 (va & L3_ADDR_BITS) >> L3_SHIFT); 504 505 idx = l0pde_index(va); 506 pde = l0[idx]; 507 508 pr("L0[%3d]=%016"PRIx64":", idx, pde); 509 db_pte_print(pde, 0, pr); 510 511 if (!l0pde_valid(pde)) 512 return; 513 514 l1 = (pd_entry_t *)AARCH64_PA_TO_KVA(l0pde_pa(pde)); 515 idx = l1pde_index(va); 516 pde = l1[idx]; 517 518 pr(" L1[%3d]=%016"PRIx64":", idx, pde); 519 db_pte_print(pde, 1, pr); 520 521 if (!l1pde_valid(pde) || l1pde_is_block(pde)) 522 return; 523 524 l2 = (pd_entry_t *)AARCH64_PA_TO_KVA(l1pde_pa(pde)); 525 idx = l2pde_index(va); 526 pde = l2[idx]; 527 528 pr(" L2[%3d]=%016"PRIx64":", idx, pde); 529 db_pte_print(pde, 2, pr); 530 531 if (!l2pde_valid(pde) || l2pde_is_block(pde)) 532 return; 533 534 l3 = (pd_entry_t *)AARCH64_PA_TO_KVA(l2pde_pa(pde)); 535 idx = l3pte_index(va); 536 pte = l3[idx]; 537 538 pr(" L3[%3d]=%016"PRIx64":", idx, pte); 539 db_pte_print(pte, 3, pr); 540 541 pa = l3pte_pa(pte); 542 pg = PHYS_TO_VM_PAGE(pa); 543 544 if (pg != NULL) { 545 uvm_page_printit(pg, false, pr); 546 547 pmap_db_mdpg_print(pg, pr); 548 } else { 549 #ifdef __HAVE_PMAP_PV_TRACK 550 if (pmap_pv_tracked(pa)) 551 pr("PV tracked"); 552 else 553 pr("No VM_PAGE or PV tracked"); 554 #else 555 pr("no VM_PAGE\n"); 556 #endif 557 } 558 } 559 560 static void 561 dump_ln_table(bool countmode, pd_entry_t *pdp, int level, int lnindex, 562 vaddr_t va, void (*pr)(const char *, ...) __printflike(1, 2)) 563 { 564 struct vm_page *pg; 565 pd_entry_t pde; 566 paddr_t pa; 567 int i, n; 568 const char *spaces[4] = { " ", " ", " ", " " }; 569 const char *spc = spaces[level]; 570 571 pa = AARCH64_KVA_TO_PA((vaddr_t)pdp); 572 pg = PHYS_TO_VM_PAGE(pa); 573 574 if (pg == NULL) { 575 pr("%sL%d: pa=%lx pg=NULL\n", spc, level, pa); 576 } else { 577 pr("%sL%d: pa=%lx pg=%p\n", spc, level, pa, pg); 578 } 579 580 for (i = n = 0; i < Ln_ENTRIES; i++) { 581 db_read_bytes((db_addr_t)&pdp[i], sizeof(pdp[i]), (char *)&pde); 582 if (lxpde_valid(pde)) { 583 if (!countmode) 584 pr("%sL%d[%3d] %3dth, va=%016lx, pte=%016lx:", 585 spc, level, i, n, va, pde); 586 n++; 587 588 if ((level != 0 && level != 3 && l1pde_is_block(pde)) || 589 (level == 3 && l3pte_is_page(pde))) { 590 if (!countmode) 591 db_pte_print(pde, level, pr); 592 } else if (level != 3 && l1pde_is_table(pde)) { 593 if (!countmode) 594 db_pte_print(pde, level, pr); 595 pa = l0pde_pa(pde); 596 dump_ln_table(countmode, 597 (pd_entry_t *)AARCH64_PA_TO_KVA(pa), 598 level + 1, i, va, pr); 599 } else { 600 if (!countmode) 601 db_pte_print(pde, level, pr); 602 } 603 } 604 605 switch (level) { 606 case 0: 607 va += L0_SIZE; 608 break; 609 case 1: 610 va += L1_SIZE; 611 break; 612 case 2: 613 va += L2_SIZE; 614 break; 615 case 3: 616 va += L3_SIZE; 617 break; 618 } 619 } 620 621 if (level == 0) 622 pr("L0 has %d entries\n", n); 623 else 624 pr("%sL%d[%3d] has %d L%d entries\n", spaces[level - 1], 625 level - 1, lnindex, n, level); 626 } 627 628 static void 629 db_dump_l0table(bool countmode, pd_entry_t *pdp, vaddr_t va_base, 630 void (*pr)(const char *, ...) __printflike(1, 2)) 631 { 632 dump_ln_table(countmode, pdp, 0, 0, va_base, pr); 633 } 634 635 void 636 db_ttbrdump(bool countmode, vaddr_t va, 637 void (*pr)(const char *, ...) __printflike(1, 2)) 638 { 639 struct pmap *pm, _pm; 640 641 pm = (struct pmap *)va; 642 db_read_bytes((db_addr_t)va, sizeof(_pm), (char *)&_pm); 643 644 pr("pmap=%p\n", pm); 645 pmap_db_pmap_print(&_pm, pr); 646 647 db_dump_l0table(countmode, pmap_l0table(pm), 648 (pm == pmap_kernel()) ? 0xffff000000000000UL : 0, pr); 649 } 650 651 void 652 cpu_Debugger(void) 653 { 654 __asm __volatile ("brk #0xffff"); 655 } 656