Home | History | Annotate | Line # | Download | only in aarch64
      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