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