Home | History | Annotate | Line # | Download | only in aarch64
      1 /* $NetBSD: db_trace.c,v 1.25 2025/09/06 06:49:49 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 <sys/cdefs.h>
     30 
     31 __KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.25 2025/09/06 06:49:49 skrll Exp $");
     32 
     33 #include <sys/param.h>
     34 #include <sys/bitops.h>
     35 #include <sys/proc.h>
     36 
     37 #include <aarch64/db_machdep.h>
     38 #include <aarch64/machdep.h>
     39 #include <aarch64/armreg.h>
     40 #include <aarch64/vmparam.h>
     41 
     42 #include <arm/cpufunc.h>
     43 
     44 #include <uvm/uvm_extern.h>
     45 
     46 #include <ddb/db_access.h>
     47 #include <ddb/db_command.h>
     48 #include <ddb/db_extern.h>
     49 #include <ddb/db_interface.h>
     50 #include <ddb/db_lwp.h>
     51 #include <ddb/db_output.h>
     52 #include <ddb/db_proc.h>
     53 #include <ddb/db_sym.h>
     54 #include <ddb/db_variables.h>
     55 
     56 #ifdef _KERNEL
     57 extern char el0_trap[];
     58 extern char el1_trap[];
     59 #else
     60 /* see also usr.sbin/crash/arch/aarch64.c */
     61 extern vaddr_t el0_trap;
     62 extern vaddr_t el1_trap;
     63 #endif
     64 
     65 #define MAXBACKTRACE	128	/* against infinite loop */
     66 
     67 
     68 __CTASSERT(VM_MIN_ADDRESS == 0);
     69 #define IN_USER_VM_ADDRESS(addr)	\
     70 	((addr) < VM_MAX_ADDRESS)
     71 #define IN_KERNEL_VM_ADDRESS(addr)	\
     72 	((VM_MIN_KERNEL_ADDRESS <= (addr)) && ((addr) < VM_MAX_KERNEL_ADDRESS))
     73 
     74 static void
     75 pr_frame(struct trapframe *tf, void (*pr)(const char *, ...) __printflike(1, 2))
     76 {
     77 	struct trapframe tf_buf;
     78 
     79 	db_read_bytes((db_addr_t)tf, sizeof(tf_buf), (char *)&tf_buf);
     80 
     81 	if (tf_buf.tf_sp == 0) {
     82 		(*pr)("---- switchframe %p (%zu bytes) ----\n",
     83 		    tf, sizeof(*tf));
     84 		dump_switchframe(tf, pr);
     85 	} else {
     86 #ifdef _KERNEL
     87 		(*pr)("---- %s: trapframe %p (%zu bytes) ----\n",
     88 		    (tf_buf.tf_esr == (uint64_t)-1) ? "Interrupt" :
     89 		    eclass_trapname(__SHIFTOUT(tf_buf.tf_esr, ESR_EC)),
     90 		    tf, sizeof(*tf));
     91 #else
     92 		(*pr)("---- trapframe %p (%zu bytes) ----\n", tf, sizeof(*tf));
     93 #endif
     94 		dump_trapframe(tf, pr);
     95 	}
     96 	(*pr)("------------------------"
     97 	      "------------------------\n");
     98 }
     99 
    100 static bool __unused
    101 is_lwp(void *p)
    102 {
    103 	lwp_t *lwp;
    104 
    105 	for (lwp = db_lwp_first(); lwp != NULL; lwp = db_lwp_next(lwp)) {
    106 		if (lwp == p)
    107 			return true;
    108 	}
    109 	return false;
    110 }
    111 
    112 static vaddr_t
    113 db_lwp_getuarea(lwp_t *l)
    114 {
    115 	void *laddr;
    116 	db_read_bytes((db_addr_t)&l->l_addr, sizeof(laddr), (char *)&laddr);
    117 	if (laddr == 0)
    118 		return 0;
    119 	return (vaddr_t)((char *)laddr - UAREA_PCB_OFFSET);
    120 }
    121 
    122 static const char *
    123 getlwpnamebysp(uint64_t sp)
    124 {
    125 	static char c_name[MAXCOMLEN];
    126 	lwp_t *lwp;
    127 	struct proc *pp;
    128 	char *lname;
    129 
    130 	for (lwp = db_lwp_first(); lwp != NULL; lwp = db_lwp_next(lwp)) {
    131 		uint64_t uarea = db_lwp_getuarea(lwp);
    132 		if ((uarea <= sp) && (sp < (uarea + USPACE))) {
    133 			db_read_bytes((db_addr_t)&lwp->l_name, sizeof(lname),
    134 			    (char *)&lname);
    135 			if (lname != NULL) {
    136 				db_read_bytes((db_addr_t)lname, sizeof(c_name),
    137 			    c_name);
    138 				return c_name;
    139 			}
    140 			db_read_bytes((db_addr_t)&lwp->l_proc, sizeof(pp),
    141 			    (char *)&pp);
    142 			if (pp != NULL) {
    143 				db_read_bytes((db_addr_t)&pp->p_comm,
    144 				    sizeof(c_name), c_name);
    145 				return c_name;
    146 			}
    147 			break;
    148 		}
    149 	}
    150 	return "unknown";
    151 }
    152 
    153 #define TRACEFLAG_LOOKUPLWP	0x00000001
    154 #define TRACEFLAG_USERSPACE	0x00000002
    155 
    156 static void
    157 pr_traceaddr(const char *prefix, uint64_t frame, uint64_t pc, int flags,
    158     void (*pr)(const char *, ...) __printflike(1, 2))
    159 {
    160 	db_expr_t offset;
    161 	db_sym_t sym;
    162 	const char *name;
    163 
    164 	sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
    165 	if (sym != DB_SYM_NULL) {
    166 		db_symbol_values(sym, &name, NULL);
    167 
    168 		if (flags & TRACEFLAG_LOOKUPLWP) {
    169 			(*pr)("%s %016lx %s %s() at %016lx ",
    170 			    prefix, frame, getlwpnamebysp(frame), name, pc);
    171 		} else {
    172 			(*pr)("%s %016lx %s() at %016lx ",
    173 			    prefix, frame, name, pc);
    174 		}
    175 		db_printsym(pc, DB_STGY_PROC, pr);
    176 		(*pr)("\n");
    177 	} else {
    178 		if (flags & TRACEFLAG_LOOKUPLWP) {
    179 			(*pr)("%s %016lx %s ?() at %016lx\n",
    180 			    prefix, frame, getlwpnamebysp(frame), pc);
    181 		} else {
    182 			(*pr)("%s %016lx ?() at %016lx\n", prefix, frame, pc);
    183 		}
    184 	}
    185 }
    186 
    187 static __inline uint64_t
    188 SignExtend(int bitwidth, uint64_t imm, unsigned int multiply)
    189 {
    190 	const uint64_t signbit = ((uint64_t)1 << (bitwidth - 1));
    191 	const uint64_t immmax = signbit << 1;
    192 
    193 	if (imm & signbit)
    194 		imm -= immmax;
    195 	return imm * multiply;
    196 }
    197 
    198 static __inline uint64_t
    199 ZeroExtend(int bitwidth, uint64_t imm, unsigned int multiply)
    200 {
    201 	return imm * multiply;
    202 }
    203 
    204 /* rotate right. if n < 0, rotate left. */
    205 static __inline uint64_t
    206 rotate(int bitwidth, uint64_t v, int n)
    207 {
    208 	uint64_t result;
    209 
    210 	n &= (bitwidth - 1);
    211 	result = (((v << (bitwidth - n)) | (v >> n)));
    212 	if (bitwidth < 64)
    213 		result &= ((1ULL << bitwidth) - 1);
    214 	return result;
    215 }
    216 
    217 static __inline uint64_t
    218 DecodeBitMasks(uint64_t sf, uint64_t n, uint64_t imms, uint64_t immr)
    219 {
    220 	const int bitwidth = (sf == 0) ? 32 : 64;
    221 	uint64_t result;
    222 	int esize, len;
    223 
    224 	len = fls64((n << 6) + (~imms & 0x3f)) - 1;
    225 	esize = (1 << len);
    226 	imms &= (esize - 1);
    227 	immr &= (esize - 1);
    228 	result = rotate(esize, (1ULL << (imms + 1)) - 1, immr);
    229 	while (esize < bitwidth) {
    230 		result |= (result << esize);
    231 		esize <<= 1;
    232 	}
    233 	if (sf == 0)
    234 		result &= ((1ULL << bitwidth) - 1);
    235 	return result;
    236 }
    237 
    238 static int
    239 analyze_func(db_addr_t func_entry, db_addr_t pc, db_addr_t sp,
    240     db_addr_t *lrp, vsize_t *stacksizep,
    241     void (*pr)(const char *, ...) __printflike(1, 2))
    242 {
    243 	vsize_t ssize = 0, lr_off = 0;
    244 	db_addr_t lr = 0;
    245 	uint64_t alloc_by_Xn_kvalue = 0;
    246 	uint64_t alloc_by_Xn_kmask = 0;
    247 	int alloc_by_Xn_reg = -1;
    248 	bool found_lr_off = false;
    249 	bool func_entry_autodetect = false;
    250 
    251 #define MAX_BACKTRACK_ANALYZE_INSN	(1024 * 4)
    252 	if (func_entry == 0) {
    253 		if (pc > MAX_BACKTRACK_ANALYZE_INSN)
    254 			func_entry = pc - MAX_BACKTRACK_ANALYZE_INSN;
    255 		else
    256 			func_entry = 4;
    257 		func_entry_autodetect = true;
    258 	};
    259 
    260 
    261 	/*
    262 	 * Locate the following instructions that allocates a stackframe.
    263 	 * Only the following patterns are supported:
    264 	 *
    265 	 *  sub sp, sp, #ALLOCSIZE		-> ssize += ALLOCSIZE
    266 	 *  sub sp, sp, #ALLOCSIZE, lsl #12	-> ssize += (ALLOCSIZE << 12)
    267 	 *
    268 	 *  mov xN, #ALLOCSIZE1
    269 	 *  (movk xN, #ALLOCSIZE2, lsl #xx)
    270 	 *  sub sp, sp, xN			-> ssize += ALLOCSIZE
    271 	 *
    272 	 *  stp x30, x??, [sp, #-ALLOCSIZE]!	-> ssize =+ ALLOCSIZE, lr_off=0
    273 	 *  stp x??, x30, [sp, #-ALLOCSIZE]!	-> ssize =+ ALLOCSIZE, lr_off=8
    274 	 *  stp x??, x??, [sp, #-ALLOCSIZE]!	-> ssize =+ ALLOCSIZE
    275 	 *
    276 	 *  str x30, [sp, #-ALLOCSIZE]!		-> ssize =+ ALLOCSIZE, lr_off=0
    277 	 *
    278 	 *  stp x30, x??, [sp, #LR_OFF]		-> lr_off = LR_OFF
    279 	 *  stp x??, x30, [sp, #LR_OFF]		-> lr_off = LR_OFF+8
    280 	 *  str x30, [sp, #LR_OFF]		-> lr_off = LR_OFF
    281 	 */
    282 
    283 /* #define BACKTRACE_ANALYZE_DEBUG */
    284 #ifdef BACKTRACE_ANALYZE_DEBUG
    285 #define TRACE_DEBUG(fmt, args...)	pr("BACKTRACE: " fmt, ## args)
    286 #else
    287 #define TRACE_DEBUG(args...)		__nothing
    288 #endif
    289 
    290 	TRACE_DEBUG("func_entry=%016lx\n", func_entry);
    291 	TRACE_DEBUG("        pc=%016lx (+%#lx)\n", pc, pc - func_entry);
    292 	TRACE_DEBUG("        sp=%016lx\n", sp);
    293 
    294 	for (pc -= 4; pc >= func_entry; pc -= 4) {
    295 		uint32_t insn;
    296 
    297 		db_read_bytes(pc, sizeof(insn), (char *)&insn);
    298 		if (insn == 0)
    299 			break;
    300 		LE32TOH(insn);
    301 
    302 		TRACE_DEBUG("INSN: %016lx: %04x\n", pc, insn);
    303 
    304 		/* "ret", "eret", or "paciasp" to detect function entry */
    305 		if (func_entry_autodetect && (
    306 		    insn == 0xd65f03e0 ||	/* "ret" */
    307 		    insn == 0xd69f03e0 ||	/* "eret" */
    308 		    insn == 0xd503233f))	/* "paciasp" */
    309 			break;
    310 
    311 		/* "sub sp,sp,#imm" or "sub sp,sp,#imm,lsl #12" */
    312 		if ((insn & 0xff8003ff) == 0xd10003ff) {
    313 			unsigned int sh = (insn >> 22) & 1;
    314 			uint64_t imm12 =
    315 			    ZeroExtend(12, (insn >> 10) & 0xfff, 1);
    316 			if (sh)
    317 				imm12 <<= 12;
    318 			ssize += imm12;
    319 			TRACE_DEBUG("sub sp,sp,#%lu\n", imm12);
    320 			continue;
    321 		}
    322 
    323 		/* sub sp,sp,Xn */
    324 		if ((insn & 0xffe0ffff) == 0xcb2063ff) {
    325 			alloc_by_Xn_reg = (insn >> 16) & 0x1f;
    326 			alloc_by_Xn_kvalue = 0;
    327 			alloc_by_Xn_kmask = 0;
    328 			TRACE_DEBUG("sub sp,sp,x%d\n", alloc_by_Xn_reg);
    329 			continue;
    330 		}
    331 		if (alloc_by_Xn_reg >= 0) {
    332 			/* movk xN,#ALLOCSIZE2,lsl #xx */
    333 			if ((insn & 0xff80001f) ==
    334 			    (0xf2800000 | alloc_by_Xn_reg)) {
    335 				int hw = (insn >> 21) & 3;
    336 				alloc_by_Xn_kvalue = ZeroExtend(16,
    337 				    (insn >> 5) & 0xffff, 1) << (hw * 16);
    338 				alloc_by_Xn_kmask = (0xffffULL << (hw * 16));
    339 				TRACE_DEBUG("movk x%d,#%#lx,lsl #%d\n",
    340 				    alloc_by_Xn_reg, alloc_by_Xn_kvalue,
    341 				    hw * 16);
    342 				continue;
    343 			}
    344 
    345 			/* (orr) mov xN,#ALLOCSIZE1 */
    346 			if ((insn & 0xff8003ff) ==
    347 			    (0xb20003e0 | alloc_by_Xn_reg)) {
    348 				uint64_t n = (insn >> 22) & 1;
    349 				uint64_t immr = (insn >> 16) & 0x3f;
    350 				uint64_t imms = (insn >> 10) & 0x3f;
    351 				uint64_t v = DecodeBitMasks(1, n, imms, immr);
    352 				TRACE_DEBUG("(orr) mov x%d,#%#lx\n",
    353 				    alloc_by_Xn_reg, v);
    354 				ssize += v;
    355 				alloc_by_Xn_reg = -1;
    356 				continue;
    357 			}
    358 
    359 			/* (movz) mov xN,#ALLOCSIZE1 */
    360 			if ((insn & 0xffe0001f) ==
    361 			    (0xd2800000 | alloc_by_Xn_reg)) {
    362 				uint64_t v =
    363 				    ZeroExtend(16, (insn >> 5) & 0xffff, 1);
    364 				TRACE_DEBUG("(movz) mov x%d,#%#lx\n",
    365 				    alloc_by_Xn_reg, v);
    366 				v &= ~alloc_by_Xn_kmask;
    367 				v |= alloc_by_Xn_kvalue;
    368 				ssize += v;
    369 				alloc_by_Xn_reg = -1;
    370 				continue;
    371 			}
    372 			/* (movn) mov xN,#ALLOCSIZE1 */
    373 			if ((insn & 0xffe0001f) ==
    374 			    (0x92800000 | alloc_by_Xn_reg)) {
    375 				uint64_t v =
    376 				    ~ZeroExtend(16, (insn >> 5) & 0xffff, 1);
    377 				TRACE_DEBUG("(movn) mov x%d,#%#lx\n",
    378 				    alloc_by_Xn_reg, v);
    379 				v &= ~alloc_by_Xn_kmask;
    380 				v |= alloc_by_Xn_kvalue;
    381 				ssize += v;
    382 				alloc_by_Xn_reg = -1;
    383 				continue;
    384 			}
    385 		}
    386 
    387 		/* stp x??,x??,[sp,#-imm7]! */
    388 		if ((insn & 0xffe003e0) == 0xa9a003e0) {
    389 			int64_t imm7 = SignExtend(7, (insn >> 15) & 0x7f, 8);
    390 			uint64_t Rt2 = (insn >> 10) & 0x1f;
    391 			uint64_t Rt1 = insn & 0x1f;
    392 			if (Rt1 == 30) {
    393 				TRACE_DEBUG("stp x30,Xn[sp,#%ld]!\n", imm7);
    394 				lr_off = ssize;
    395 				ssize += -imm7;
    396 				found_lr_off = true;
    397 			} else if (Rt2 == 30) {
    398 				TRACE_DEBUG("stp Xn,x30,[sp,#%ld]!\n", imm7);
    399 				lr_off = ssize + 8;
    400 				ssize += -imm7;
    401 				found_lr_off = true;
    402 			} else {
    403 				ssize += -imm7;
    404 				TRACE_DEBUG("stp Xn,Xn,[sp,#%ld]!\n", imm7);
    405 			}
    406 
    407 			/*
    408 			 * "stp x29,x30,[sp,#-n]!" is the code to create
    409 			 * a frame pointer at the beginning of the function.
    410 			 */
    411 			if (func_entry_autodetect && Rt1 == 29 && Rt2 == 30)
    412 				break;
    413 
    414 			continue;
    415 		}
    416 
    417 		/* stp x??,x??,[sp,#imm7] */
    418 		if ((insn & 0xffc003e0) == 0xa90003e0) {
    419 			int64_t imm7 = SignExtend(7, (insn >> 15) & 0x7f, 8);
    420 			uint64_t Rt2 = (insn >> 10) & 0x1f;
    421 			uint64_t Rt1 = insn & 0x1f;
    422 			if (Rt1 == 30) {
    423 				lr_off = ssize + imm7;
    424 				found_lr_off = true;
    425 				TRACE_DEBUG("stp x30,X%lu[sp,#%ld]\n",
    426 				    Rt2, imm7);
    427 				TRACE_DEBUG("lr off = %lu = %#lx\n",
    428 				    lr_off, lr_off);
    429 			} else if (Rt2 == 30) {
    430 				lr_off = ssize + imm7 + 8;
    431 				found_lr_off = true;
    432 				TRACE_DEBUG("stp X%lu,x30,[sp,#%ld]\n",
    433 				    Rt1, imm7);
    434 				TRACE_DEBUG("lr off = %lu = %#lx\n",
    435 				    lr_off, lr_off);
    436 			}
    437 			continue;
    438 		}
    439 
    440 		/* str x30,[sp,#imm12] */
    441 		if ((insn & 0xffc003ff) == 0xf90003fe) {
    442 			uint64_t imm12 =
    443 			    ZeroExtend(12, (insn >> 10) & 0xfff, 8);
    444 			lr_off = ssize + imm12;
    445 			found_lr_off = true;
    446 			TRACE_DEBUG("str x30,[sp,#%lu]\n", imm12);
    447 			TRACE_DEBUG("lr off = %lu = %#lx\n", lr_off, lr_off);
    448 			continue;
    449 		}
    450 
    451 		/* str x30,[sp,#-imm9]! */
    452 		if ((insn & 0xfff00fff) == 0xf8100ffe) {
    453 			int64_t imm9 = SignExtend(9, (insn >> 12) & 0x1ff, 1);
    454 			lr_off = ssize;
    455 			ssize += -imm9;
    456 			found_lr_off = true;
    457 			TRACE_DEBUG("str x30,[sp,#%ld]!\n", imm9);
    458 			TRACE_DEBUG("lr off = %lu = %#lx\n", lr_off, lr_off);
    459 			continue;
    460 		}
    461 	}
    462 
    463 	if (found_lr_off) {
    464 		if (lr_off >= ssize) {
    465 			pr("cannot locate return address\n");
    466 			return -1;
    467 		}
    468 		db_read_bytes((db_addr_t)sp + lr_off, sizeof(lr), (char *)&lr);
    469 		lr = aarch64_strip_pac(lr);
    470 	}
    471 	*stacksizep = ssize;
    472 	*lrp = lr;
    473 
    474 	TRACE_DEBUG("-----------\n");
    475 	TRACE_DEBUG("       sp: %#lx\n", sp);
    476 	TRACE_DEBUG("stacksize: %#06lx = %lu\n", ssize, ssize);
    477 	TRACE_DEBUG("lr offset: %#06lx = %lu\n", lr_off, lr_off);
    478 	TRACE_DEBUG("   new lr: %#lx\n", lr);
    479 	TRACE_DEBUG("===========\n\n");
    480 
    481 	return 0;
    482 }
    483 
    484 /*
    485  * Backtrace without framepointer ($fp).
    486  *
    487  * Examines the contents of a function and returns the stack size allocated
    488  * by the function and the stored $lr.
    489  *
    490  * This works well for code compiled with -fomit-frame-pointer.
    491  */
    492 static void
    493 db_sp_trace(struct trapframe *tf, db_addr_t fp, db_expr_t count, int flags,
    494     void (*pr)(const char *, ...) __printflike(1, 2))
    495 {
    496 	struct trapframe tf_buf;
    497 	db_addr_t pc, sp, lr0;
    498 	bool allow_leaf_function = false;
    499 
    500 	if (tf == NULL) {
    501 		/*
    502 		 * In the case of "trace/s <frame-address>",
    503 		 * the specified frame pointer address is considered
    504 		 * a trapframe (or a switchframe) address.
    505 		 */
    506 		tf = (struct trapframe *)fp;
    507 	}
    508 
    509 	pr_frame(tf, pr);
    510 
    511 	db_read_bytes((db_addr_t)tf, sizeof(tf_buf), (char *)&tf_buf);
    512 	if (tf_buf.tf_sp == 0) {
    513 		/* switchframe */
    514 		lr0 = 0;
    515 		pc = aarch64_strip_pac(tf_buf.tf_lr) - 4;
    516 		sp = (uint64_t)(tf + 1);
    517 	} else {
    518 		/* trapframe */
    519 		lr0 = aarch64_strip_pac(tf_buf.tf_lr);
    520 		pc = tf_buf.tf_pc;
    521 		sp = tf_buf.tf_sp;
    522 		allow_leaf_function = true;
    523 	}
    524 
    525 	TRACE_DEBUG("pc =%016lx\n", pc);
    526 	TRACE_DEBUG("sp =%016lx\n", sp);
    527 	TRACE_DEBUG("lr0=%016lx\n", lr0);
    528 
    529 	for (; (count > 0) && (sp != 0); count--) {
    530 		if ((pc == (db_addr_t)el0_trap) ||
    531 		    (pc == (db_addr_t)el1_trap)) {
    532 
    533 			pr_traceaddr("tf", sp, pc, flags, pr);
    534 
    535 			db_read_bytes((db_addr_t)sp, sizeof(tf_buf),
    536 			    (char *)&tf_buf);
    537 			if (tf_buf.tf_lr == 0)
    538 				break;
    539 			pr_frame((struct trapframe *)sp, pr);
    540 
    541 			sp = tf_buf.tf_sp;
    542 			pc = tf_buf.tf_pc;
    543 			if (pc == 0)
    544 				pc = aarch64_strip_pac(tf_buf.tf_lr) - 4;
    545 			if (pc == 0)
    546 				break;
    547 			lr0 = aarch64_strip_pac(tf_buf.tf_lr);
    548 			allow_leaf_function = true;
    549 
    550 		} else {
    551 			db_sym_t sym;
    552 			db_addr_t func_entry, lr;
    553 			db_expr_t func_offset;
    554 			vsize_t stacksize;
    555 
    556 			pr_traceaddr("sp", sp, pc, flags, pr);
    557 
    558 			if ((flags & TRACEFLAG_USERSPACE) == 0 &&
    559 			    !IN_KERNEL_VM_ADDRESS(pc))
    560 				break;
    561 
    562 			sym = db_search_symbol(pc, DB_STGY_ANY, &func_offset);
    563 			if (sym != 0) {
    564 				func_entry = pc - func_offset;
    565 				if (func_entry ==
    566 				    (db_addr_t)cpu_switchto_softint) {
    567 					/*
    568 					 * In cpu_switchto_softint(), backtrace
    569 					 * information for DDB is pushed onto
    570 					 * the stack.
    571 					 */
    572 					db_read_bytes((db_addr_t)sp + 8,
    573 					    sizeof(pc), (char *)&pc);
    574 					db_read_bytes((db_addr_t)sp,
    575 					    sizeof(sp), (char *)&sp);
    576 					continue;
    577 				}
    578 			} else {
    579 				func_entry = 0;	/* autodetect mode */
    580 			}
    581 
    582 			if (analyze_func(func_entry, pc, sp, &lr, &stacksize,
    583 			    pr) != 0)
    584 				break;
    585 
    586 			if (allow_leaf_function) {
    587 				if (lr == 0)
    588 					lr = lr0;
    589 				allow_leaf_function = false;
    590 			} else {
    591 				if (lr == 0)
    592 					break;
    593 			}
    594 
    595 			sp += stacksize;
    596 			pc = lr - 4;
    597 		}
    598 	}
    599 }
    600 
    601 static void
    602 db_fp_trace(struct trapframe *tf, db_addr_t fp, db_expr_t count, int flags,
    603     void (*pr)(const char *, ...) __printflike(1, 2))
    604 {
    605 	uint64_t lr;
    606 	uint64_t lastlr, lastfp;
    607 
    608 	if (tf != NULL) {
    609 		pr_frame(tf, pr);
    610 		lastfp = lastlr = lr = fp = 0;
    611 
    612 		db_read_bytes((db_addr_t)&tf->tf_pc, sizeof(lr), (char *)&lr);
    613 		db_read_bytes((db_addr_t)&tf->tf_reg[29], sizeof(fp), (char *)&fp);
    614 		lr = aarch64_strip_pac(lr);
    615 
    616 		pr_traceaddr("fp", fp, lr - 4, flags, pr);
    617 	}
    618 
    619 	for (; (count > 0) && (fp != 0); count--) {
    620 
    621 		lastfp = fp;
    622 		fp = lr = 0;
    623 		/*
    624 		 * normal stack frame
    625 		 *  fp[0]  saved fp(x29) value
    626 		 *  fp[1]  saved lr(x30) value
    627 		 */
    628 		db_read_bytes(lastfp + 0, sizeof(fp), (char *)&fp);
    629 		db_read_bytes(lastfp + 8, sizeof(lr), (char *)&lr);
    630 		lr = aarch64_strip_pac(lr);
    631 
    632 		if (lr == 0 || ((flags & TRACEFLAG_USERSPACE) == 0 &&
    633 		    IN_USER_VM_ADDRESS(lr)))
    634 			break;
    635 
    636 		if (((char *)(lr - 4) == (char *)el0_trap) ||
    637 		    ((char *)(lr - 4) == (char *)el1_trap)) {
    638 
    639 			tf = (struct trapframe *)fp;
    640 
    641 			lastfp = (uint64_t)tf;
    642 			lastlr = lr;
    643 			lr = fp = 0;
    644 			db_read_bytes((db_addr_t)&tf->tf_pc, sizeof(lr),
    645 			    (char *)&lr);
    646 			if (lr == 0) {
    647 				/*
    648 				 * The exception may have been from a
    649 				 * jump to null, so the null pc we
    650 				 * would return to is useless.  Try
    651 				 * x[30] instead -- that will be the
    652 				 * return address for the jump.
    653 				 */
    654 				db_read_bytes((db_addr_t)&tf->tf_reg[30],
    655 				    sizeof(lr), (char *)&lr);
    656 			}
    657 			db_read_bytes((db_addr_t)&tf->tf_reg[29], sizeof(fp),
    658 			    (char *)&fp);
    659 			lr = aarch64_strip_pac(lr);
    660 
    661 			pr_traceaddr("tf", (db_addr_t)tf, lastlr - 4, flags, pr);
    662 
    663 			if (lr == 0)
    664 				break;
    665 
    666 			pr_frame(tf, pr);
    667 			tf = NULL;
    668 
    669 			if ((flags & TRACEFLAG_USERSPACE) == 0 &&
    670 			    IN_USER_VM_ADDRESS(lr))
    671 				break;
    672 
    673 			pr_traceaddr("fp", fp, lr, flags, pr);
    674 		} else {
    675 			pr_traceaddr("fp", fp, lr - 4, flags, pr);
    676 		}
    677 	}
    678 }
    679 
    680 void
    681 db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
    682     const char *modif, void (*pr)(const char *, ...) __printflike(1, 2))
    683 {
    684 	uint64_t fp;
    685 	struct trapframe *tf = NULL;
    686 	int flags = 0;
    687 	bool trace_thread = false;
    688 	bool trace_lwp = false;
    689 	bool trace_sp = false;
    690 
    691 	for (; *modif != '\0'; modif++) {
    692 		switch (*modif) {
    693 		case 'a':
    694 			trace_lwp = true;
    695 			trace_thread = false;
    696 			break;
    697 		case 'l':
    698 			break;
    699 		case 't':
    700 			trace_thread = true;
    701 			trace_lwp = false;
    702 			break;
    703 		case 's':
    704 			trace_sp = true;
    705 			break;
    706 		case 'u':
    707 			flags |= TRACEFLAG_USERSPACE;
    708 			break;
    709 		case 'x':
    710 			flags |= TRACEFLAG_LOOKUPLWP;
    711 			break;
    712 		default:
    713 			pr("usage: bt[/ulsx] [frame-address][,count]\n");
    714 			pr("       bt/t[ulsx] [pid][,count]\n");
    715 			pr("       bt/a[ulsx] [lwpaddr][,count]\n");
    716 			pr("\n");
    717 			pr("       /s      trace without framepointer\n");
    718 			pr("       /x      reverse lookup lwp name from sp\n");
    719 			return;
    720 		}
    721 	}
    722 
    723 #if defined(_KERNEL)
    724 	if (!have_addr) {
    725 		if (trace_lwp) {
    726 			addr = (db_expr_t)curlwp;
    727 		} else if (trace_thread) {
    728 			addr = curlwp->l_proc->p_pid;
    729 		} else {
    730 			tf = DDB_REGS;
    731 		}
    732 	}
    733 #endif
    734 
    735 	if (trace_thread) {
    736 		proc_t *pp;
    737 
    738 		if ((pp = db_proc_find((pid_t)addr)) == 0) {
    739 			(*pr)("trace: pid %d: not found\n", (int)addr);
    740 			return;
    741 		}
    742 		db_read_bytes((db_addr_t)pp + offsetof(proc_t, p_lwps.lh_first),
    743 		    sizeof(addr), (char *)&addr);
    744 		trace_thread = false;
    745 		trace_lwp = true;
    746 	}
    747 
    748 #if 0
    749 	/* "/a" is abbreviated? */
    750 	if (!trace_lwp && is_lwp(addr))
    751 		trace_lwp = true;
    752 #endif
    753 
    754 	if (trace_lwp) {
    755 		struct lwp l;
    756 		pid_t pid;
    757 
    758 		db_read_bytes(addr, sizeof(l), (char *)&l);
    759 		db_read_bytes((db_addr_t)l.l_proc + offsetof(proc_t, p_pid),
    760 		    sizeof(pid), (char *)&pid);
    761 
    762 #if defined(_KERNEL)
    763 		if (addr == (db_expr_t)curlwp) {
    764 			fp = (uint64_t)&DDB_REGS->tf_reg[29];	/* &reg[29]={fp,lr} */
    765 			tf = DDB_REGS;
    766 			(*pr)("trace: pid %d lid %d (curlwp) at tf %p\n",
    767 			    pid, l.l_lid, tf);
    768 		} else
    769 #endif
    770 		{
    771 			struct pcb *pcb = lwp_getpcb(&l);
    772 
    773 			db_read_bytes((db_addr_t)pcb +
    774 			    offsetof(struct pcb, pcb_tf),
    775 			    sizeof(tf), (char *)&tf);
    776 			if (tf != 0) {
    777 				db_read_bytes((db_addr_t)&tf->tf_reg[29],
    778 				    sizeof(fp), (char *)&fp);
    779 				(*pr)("trace: pid %d lid %d at tf %p (in pcb)\n",
    780 				    pid, l.l_lid, tf);
    781 			}
    782 #if defined(MULTIPROCESSOR) && defined(_KERNEL)
    783 			else if (l.l_stat == LSONPROC ||
    784 			    (l.l_pflag & LP_RUNNING) != 0) {
    785 
    786 				/* running lwp on other cpus */
    787 				extern struct trapframe *db_readytoswitch[];
    788 				u_int index;
    789 
    790 				db_read_bytes((db_addr_t)l.l_cpu +
    791 				    offsetof(struct cpu_info, ci_index),
    792 				    sizeof(index), (char *)&index);
    793 				tf = db_readytoswitch[index];
    794 
    795 				(*pr)("trace: pid %d lid %d at tf %p (in kdb_trap)\n",
    796 				    pid, l.l_lid, tf);
    797 			}
    798 #endif
    799 			else {
    800 				(*pr)("trace: no trapframe found for lwp: %p\n", (void *)addr);
    801 			}
    802 		}
    803 	} else if (tf == NULL) {
    804 		fp = addr;
    805 		pr("trace fp %016lx\n", fp);
    806 	} else {
    807 		pr("trace tf %p\n", tf);
    808 	}
    809 
    810 	if (count > MAXBACKTRACE)
    811 		count = MAXBACKTRACE;
    812 
    813 	if (trace_sp) {
    814 		/* trace $lr pushed to sp */
    815 		db_sp_trace(tf, fp, count, flags, pr);
    816 	} else {
    817 		/* trace $fp linked list */
    818 		db_fp_trace(tf, fp, count, flags, pr);
    819 	}
    820 }
    821