xref: /openbmc/linux/arch/alpha/lib/stacktrace.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds #include <linux/kernel.h>
31da177e4SLinus Torvalds 
41da177e4SLinus Torvalds typedef unsigned int instr;
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds #define MAJOR_OP	0xfc000000
71da177e4SLinus Torvalds #define LDA_OP		0x20000000
81da177e4SLinus Torvalds #define STQ_OP		0xb4000000
91da177e4SLinus Torvalds #define BR_OP		0xc0000000
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #define STK_ALLOC_1	0x23de8000 /* lda $30,-X($30) */
121da177e4SLinus Torvalds #define STK_ALLOC_1M	0xffff8000
131da177e4SLinus Torvalds #define STK_ALLOC_2	0x43c0153e /* subq $30,X,$30 */
141da177e4SLinus Torvalds #define STK_ALLOC_2M	0xffe01fff
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #define MEM_REG		0x03e00000
171da177e4SLinus Torvalds #define MEM_BASE	0x001f0000
181da177e4SLinus Torvalds #define MEM_OFF		0x0000ffff
191da177e4SLinus Torvalds #define MEM_OFF_SIGN	0x00008000
201da177e4SLinus Torvalds #define	BASE_SP		0x001e0000
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #define STK_ALLOC_MATCH(INSTR)			\
231da177e4SLinus Torvalds   (((INSTR) & STK_ALLOC_1M) == STK_ALLOC_1	\
241da177e4SLinus Torvalds    || ((INSTR) & STK_ALLOC_2M) == STK_ALLOC_2)
251da177e4SLinus Torvalds #define STK_PUSH_MATCH(INSTR) \
261da177e4SLinus Torvalds   (((INSTR) & (MAJOR_OP | MEM_BASE | MEM_OFF_SIGN)) == (STQ_OP | BASE_SP))
271da177e4SLinus Torvalds #define MEM_OP_OFFSET(INSTR) \
281da177e4SLinus Torvalds   (((long)((INSTR) & MEM_OFF) << 48) >> 48)
291da177e4SLinus Torvalds #define MEM_OP_REG(INSTR) \
301da177e4SLinus Torvalds   (((INSTR) & MEM_REG) >> 22)
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds /* Branches, jumps, PAL calls, and illegal opcodes end a basic block. */
331da177e4SLinus Torvalds #define BB_END(INSTR)						\
341da177e4SLinus Torvalds   (((instr)(INSTR) >= BR_OP) | ((instr)(INSTR) < LDA_OP) |	\
351da177e4SLinus Torvalds    ((((instr)(INSTR) ^ 0x60000000) < 0x20000000) &		\
361da177e4SLinus Torvalds     (((instr)(INSTR) & 0x0c000000) != 0)))
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds #define IS_KERNEL_TEXT(PC) ((unsigned long)(PC) > START_ADDR)
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds static char reg_name[][4] = {
411da177e4SLinus Torvalds 	"v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ", "t7 ",
421da177e4SLinus Torvalds 	"s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "s6 ", "a0 ", "a1 ",
431da177e4SLinus Torvalds 	"a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ", "t10", "t11", "ra ",
441da177e4SLinus Torvalds 	"pv ", "at ", "gp ", "sp ", "0"
451da177e4SLinus Torvalds };
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds static instr *
display_stored_regs(instr * pro_pc,unsigned char * sp)491da177e4SLinus Torvalds display_stored_regs(instr * pro_pc, unsigned char * sp)
501da177e4SLinus Torvalds {
511da177e4SLinus Torvalds 	instr * ret_pc = 0;
521da177e4SLinus Torvalds 	int reg;
531da177e4SLinus Torvalds 	unsigned long value;
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	printk("Prologue [<%p>], Frame %p:\n", pro_pc, sp);
561da177e4SLinus Torvalds 	while (!BB_END(*pro_pc))
571da177e4SLinus Torvalds 		if (STK_PUSH_MATCH(*pro_pc)) {
581da177e4SLinus Torvalds 			reg = (*pro_pc & MEM_REG) >> 21;
591da177e4SLinus Torvalds 			value = *(unsigned long *)(sp + (*pro_pc & MEM_OFF));
601da177e4SLinus Torvalds 			if (reg == 26)
611da177e4SLinus Torvalds 				ret_pc = (instr *)value;
621da177e4SLinus Torvalds 			printk("\t\t%s / 0x%016lx\n", reg_name[reg], value);
631da177e4SLinus Torvalds 		}
641da177e4SLinus Torvalds 	return ret_pc;
651da177e4SLinus Torvalds }
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds static instr *
seek_prologue(instr * pc)681da177e4SLinus Torvalds seek_prologue(instr * pc)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds 	while (!STK_ALLOC_MATCH(*pc))
711da177e4SLinus Torvalds 		--pc;
721da177e4SLinus Torvalds 	while (!BB_END(*(pc - 1)))
731da177e4SLinus Torvalds 		--pc;
741da177e4SLinus Torvalds 	return pc;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds static long
stack_increment(instr * prologue_pc)781da177e4SLinus Torvalds stack_increment(instr * prologue_pc)
791da177e4SLinus Torvalds {
801da177e4SLinus Torvalds 	while (!STK_ALLOC_MATCH(*prologue_pc))
811da177e4SLinus Torvalds 		++prologue_pc;
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	/* Count the bytes allocated. */
841da177e4SLinus Torvalds 	if ((*prologue_pc & STK_ALLOC_1M) == STK_ALLOC_1M)
851da177e4SLinus Torvalds 		return -(((long)(*prologue_pc) << 48) >> 48);
861da177e4SLinus Torvalds 	else
871da177e4SLinus Torvalds 		return (*prologue_pc >> 13) & 0xff;
881da177e4SLinus Torvalds }
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds void
stacktrace(void)911da177e4SLinus Torvalds stacktrace(void)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds 	instr * ret_pc;
941da177e4SLinus Torvalds 	instr * prologue = (instr *)stacktrace;
95*bd1912deSKees Cook 	unsigned char *sp = (unsigned char *)current_stack_pointer;
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds 	printk("\tstack trace:\n");
981da177e4SLinus Torvalds 	do {
991da177e4SLinus Torvalds 		ret_pc = display_stored_regs(prologue, sp);
1001da177e4SLinus Torvalds 		sp += stack_increment(prologue);
1011da177e4SLinus Torvalds 		prologue = seek_prologue(ret_pc);
1021da177e4SLinus Torvalds 	} while (IS_KERNEL_TEXT(ret_pc));
1031da177e4SLinus Torvalds }
104