1#include <linux/oprofile.h> 2#include <linux/sched.h> 3#include <linux/mm.h> 4#include <linux/uaccess.h> 5#include <asm/ptrace.h> 6#include <asm/stacktrace.h> 7#include <linux/stacktrace.h> 8#include <linux/kernel.h> 9#include <asm/sections.h> 10#include <asm/inst.h> 11 12struct stackframe { 13 unsigned long sp; 14 unsigned long pc; 15 unsigned long ra; 16}; 17 18static inline int get_mem(unsigned long addr, unsigned long *result) 19{ 20 unsigned long *address = (unsigned long *) addr; 21 if (!access_ok(VERIFY_READ, addr, sizeof(unsigned long))) 22 return -1; 23 if (__copy_from_user_inatomic(result, address, sizeof(unsigned long))) 24 return -3; 25 return 0; 26} 27 28/* 29 * These two instruction helpers were taken from process.c 30 */ 31static inline int is_ra_save_ins(union mips_instruction *ip) 32{ 33 /* sw / sd $ra, offset($sp) */ 34 return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) 35 && ip->i_format.rs == 29 && ip->i_format.rt == 31; 36} 37 38static inline int is_sp_move_ins(union mips_instruction *ip) 39{ 40 /* addiu/daddiu sp,sp,-imm */ 41 if (ip->i_format.rs != 29 || ip->i_format.rt != 29) 42 return 0; 43 if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op) 44 return 1; 45 return 0; 46} 47 48/* 49 * Looks for specific instructions that mark the end of a function. 50 * This usually means we ran into the code area of the previous function. 51 */ 52static inline int is_end_of_function_marker(union mips_instruction *ip) 53{ 54 /* jr ra */ 55 if (ip->r_format.func == jr_op && ip->r_format.rs == 31) 56 return 1; 57 /* lui gp */ 58 if (ip->i_format.opcode == lui_op && ip->i_format.rt == 28) 59 return 1; 60 return 0; 61} 62 63/* 64 * TODO for userspace stack unwinding: 65 * - handle cases where the stack is adjusted inside a function 66 * (generally doesn't happen) 67 * - find optimal value for max_instr_check 68 * - try to find a better way to handle leaf functions 69 */ 70 71static inline int unwind_user_frame(struct stackframe *old_frame, 72 const unsigned int max_instr_check) 73{ 74 struct stackframe new_frame = *old_frame; 75 off_t ra_offset = 0; 76 size_t stack_size = 0; 77 unsigned long addr; 78 79 if (old_frame->pc == 0 || old_frame->sp == 0 || old_frame->ra == 0) 80 return -9; 81 82 for (addr = new_frame.pc; (addr + max_instr_check > new_frame.pc) 83 && (!ra_offset || !stack_size); --addr) { 84 union mips_instruction ip; 85 86 if (get_mem(addr, (unsigned long *) &ip)) 87 return -11; 88 89 if (is_sp_move_ins(&ip)) { 90 int stack_adjustment = ip.i_format.simmediate; 91 if (stack_adjustment > 0) 92 /* This marks the end of the previous function, 93 which means we overran. */ 94 break; 95 stack_size = (unsigned long) stack_adjustment; 96 } else if (is_ra_save_ins(&ip)) { 97 int ra_slot = ip.i_format.simmediate; 98 if (ra_slot < 0) 99 /* This shouldn't happen. */ 100 break; 101 ra_offset = ra_slot; 102 } else if (is_end_of_function_marker(&ip)) 103 break; 104 } 105 106 if (!ra_offset || !stack_size) 107 goto done; 108 109 if (ra_offset) { 110 new_frame.ra = old_frame->sp + ra_offset; 111 if (get_mem(new_frame.ra, &(new_frame.ra))) 112 return -13; 113 } 114 115 if (stack_size) { 116 new_frame.sp = old_frame->sp + stack_size; 117 if (get_mem(new_frame.sp, &(new_frame.sp))) 118 return -14; 119 } 120 121 if (new_frame.sp > old_frame->sp) 122 return -2; 123 124done: 125 new_frame.pc = old_frame->ra; 126 *old_frame = new_frame; 127 128 return 0; 129} 130 131static inline void do_user_backtrace(unsigned long low_addr, 132 struct stackframe *frame, 133 unsigned int depth) 134{ 135 const unsigned int max_instr_check = 512; 136 const unsigned long high_addr = low_addr + THREAD_SIZE; 137 138 while (depth-- && !unwind_user_frame(frame, max_instr_check)) { 139 oprofile_add_trace(frame->ra); 140 if (frame->sp < low_addr || frame->sp > high_addr) 141 break; 142 } 143} 144 145#ifndef CONFIG_KALLSYMS 146static inline void do_kernel_backtrace(unsigned long low_addr, 147 struct stackframe *frame, 148 unsigned int depth) { } 149#else 150static inline void do_kernel_backtrace(unsigned long low_addr, 151 struct stackframe *frame, 152 unsigned int depth) 153{ 154 while (depth-- && frame->pc) { 155 frame->pc = unwind_stack_by_address(low_addr, 156 &(frame->sp), 157 frame->pc, 158 &(frame->ra)); 159 oprofile_add_trace(frame->ra); 160 } 161} 162#endif 163 164void notrace op_mips_backtrace(struct pt_regs *const regs, unsigned int depth) 165{ 166 struct stackframe frame = { .sp = regs->regs[29], 167 .pc = regs->cp0_epc, 168 .ra = regs->regs[31] }; 169 const int userspace = user_mode(regs); 170 const unsigned long low_addr = ALIGN(frame.sp, THREAD_SIZE); 171 172 if (userspace) 173 do_user_backtrace(low_addr, &frame, depth); 174 else 175 do_kernel_backtrace(low_addr, &frame, depth); 176} 177