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