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 * 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 * 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 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 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