1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2022 Loongson Technology Corporation Limited 4 */ 5 #include <linux/kallsyms.h> 6 7 #include <asm/inst.h> 8 #include <asm/ptrace.h> 9 #include <asm/unwind.h> 10 11 unsigned long unwind_get_return_address(struct unwind_state *state) 12 { 13 14 if (unwind_done(state)) 15 return 0; 16 else if (state->type) 17 return state->pc; 18 else if (state->first) 19 return state->pc; 20 21 return *(unsigned long *)(state->sp); 22 23 } 24 EXPORT_SYMBOL_GPL(unwind_get_return_address); 25 26 static bool unwind_by_guess(struct unwind_state *state) 27 { 28 struct stack_info *info = &state->stack_info; 29 unsigned long addr; 30 31 for (state->sp += sizeof(unsigned long); 32 state->sp < info->end; 33 state->sp += sizeof(unsigned long)) { 34 addr = *(unsigned long *)(state->sp); 35 if (__kernel_text_address(addr)) 36 return true; 37 } 38 39 return false; 40 } 41 42 static bool unwind_by_prologue(struct unwind_state *state) 43 { 44 struct stack_info *info = &state->stack_info; 45 union loongarch_instruction *ip, *ip_end; 46 long frame_ra = -1; 47 unsigned long frame_size = 0; 48 unsigned long size, offset, pc = state->pc; 49 50 if (state->sp >= info->end || state->sp < info->begin) 51 return false; 52 53 if (!kallsyms_lookup_size_offset(pc, &size, &offset)) 54 return false; 55 56 ip = (union loongarch_instruction *)(pc - offset); 57 ip_end = (union loongarch_instruction *)pc; 58 59 while (ip < ip_end) { 60 if (is_stack_alloc_ins(ip)) { 61 frame_size = (1 << 12) - ip->reg2i12_format.immediate; 62 ip++; 63 break; 64 } 65 ip++; 66 } 67 68 if (!frame_size) { 69 if (state->first) 70 goto first; 71 72 return false; 73 } 74 75 while (ip < ip_end) { 76 if (is_ra_save_ins(ip)) { 77 frame_ra = ip->reg2i12_format.immediate; 78 break; 79 } 80 if (is_branch_ins(ip)) 81 break; 82 ip++; 83 } 84 85 if (frame_ra < 0) { 86 if (state->first) { 87 state->sp = state->sp + frame_size; 88 goto first; 89 } 90 return false; 91 } 92 93 if (state->first) 94 state->first = false; 95 96 state->pc = *(unsigned long *)(state->sp + frame_ra); 97 state->sp = state->sp + frame_size; 98 return !!__kernel_text_address(state->pc); 99 100 first: 101 state->first = false; 102 if (state->pc == state->ra) 103 return false; 104 105 state->pc = state->ra; 106 107 return !!__kernel_text_address(state->ra); 108 } 109 110 void unwind_start(struct unwind_state *state, struct task_struct *task, 111 struct pt_regs *regs) 112 { 113 memset(state, 0, sizeof(*state)); 114 115 if (regs && __kernel_text_address(regs->csr_era)) { 116 state->pc = regs->csr_era; 117 state->sp = regs->regs[3]; 118 state->ra = regs->regs[1]; 119 state->type = UNWINDER_PROLOGUE; 120 } 121 122 state->task = task; 123 state->first = true; 124 125 get_stack_info(state->sp, state->task, &state->stack_info); 126 127 if (!unwind_done(state) && !__kernel_text_address(state->pc)) 128 unwind_next_frame(state); 129 } 130 EXPORT_SYMBOL_GPL(unwind_start); 131 132 bool unwind_next_frame(struct unwind_state *state) 133 { 134 struct stack_info *info = &state->stack_info; 135 struct pt_regs *regs; 136 unsigned long pc; 137 138 if (unwind_done(state)) 139 return false; 140 141 do { 142 switch (state->type) { 143 case UNWINDER_GUESS: 144 state->first = false; 145 if (unwind_by_guess(state)) 146 return true; 147 break; 148 149 case UNWINDER_PROLOGUE: 150 if (unwind_by_prologue(state)) 151 return true; 152 153 if (info->type == STACK_TYPE_IRQ && 154 info->end == state->sp) { 155 regs = (struct pt_regs *)info->next_sp; 156 pc = regs->csr_era; 157 158 if (user_mode(regs) || !__kernel_text_address(pc)) 159 return false; 160 161 state->pc = pc; 162 state->sp = regs->regs[3]; 163 state->ra = regs->regs[1]; 164 state->first = true; 165 get_stack_info(state->sp, state->task, info); 166 167 return true; 168 } 169 } 170 171 state->sp = info->next_sp; 172 173 } while (!get_stack_info(state->sp, state->task, info)); 174 175 return false; 176 } 177 EXPORT_SYMBOL_GPL(unwind_next_frame); 178