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