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 unsigned long frame_size = 0, frame_ra = -1; 47 unsigned long size, offset, pc = state->pc; 48 49 if (state->sp >= info->end || state->sp < info->begin) 50 return false; 51 52 if (!kallsyms_lookup_size_offset(pc, &size, &offset)) 53 return false; 54 55 ip = (union loongarch_instruction *)(pc - offset); 56 ip_end = (union loongarch_instruction *)pc; 57 58 while (ip < ip_end) { 59 if (is_stack_alloc_ins(ip)) { 60 frame_size = (1 << 12) - ip->reg2i12_format.immediate; 61 ip++; 62 break; 63 } 64 ip++; 65 } 66 67 if (!frame_size) { 68 if (state->first) 69 goto first; 70 71 return false; 72 } 73 74 while (ip < ip_end) { 75 if (is_ra_save_ins(ip)) { 76 frame_ra = ip->reg2i12_format.immediate; 77 break; 78 } 79 if (is_branch_ins(ip)) 80 break; 81 ip++; 82 } 83 84 if (frame_ra < 0) { 85 if (state->first) { 86 state->sp = state->sp + frame_size; 87 goto first; 88 } 89 return false; 90 } 91 92 if (state->first) 93 state->first = false; 94 95 state->pc = *(unsigned long *)(state->sp + frame_ra); 96 state->sp = state->sp + frame_size; 97 return !!__kernel_text_address(state->pc); 98 99 first: 100 state->first = false; 101 if (state->pc == state->ra) 102 return false; 103 104 state->pc = state->ra; 105 106 return !!__kernel_text_address(state->ra); 107 } 108 109 void unwind_start(struct unwind_state *state, struct task_struct *task, 110 struct pt_regs *regs) 111 { 112 memset(state, 0, sizeof(*state)); 113 114 if (regs && __kernel_text_address(regs->csr_era)) { 115 state->pc = regs->csr_era; 116 state->sp = regs->regs[3]; 117 state->ra = regs->regs[1]; 118 state->type = UNWINDER_PROLOGUE; 119 } 120 121 state->task = task; 122 state->first = true; 123 124 get_stack_info(state->sp, state->task, &state->stack_info); 125 126 if (!unwind_done(state) && !__kernel_text_address(state->pc)) 127 unwind_next_frame(state); 128 } 129 EXPORT_SYMBOL_GPL(unwind_start); 130 131 bool unwind_next_frame(struct unwind_state *state) 132 { 133 struct stack_info *info = &state->stack_info; 134 struct pt_regs *regs; 135 unsigned long pc; 136 137 if (unwind_done(state)) 138 return false; 139 140 do { 141 switch (state->type) { 142 case UNWINDER_GUESS: 143 state->first = false; 144 if (unwind_by_guess(state)) 145 return true; 146 break; 147 148 case UNWINDER_PROLOGUE: 149 if (unwind_by_prologue(state)) 150 return true; 151 152 if (info->type == STACK_TYPE_IRQ && 153 info->end == state->sp) { 154 regs = (struct pt_regs *)info->next_sp; 155 pc = regs->csr_era; 156 157 if (user_mode(regs) || !__kernel_text_address(pc)) 158 return false; 159 160 state->pc = pc; 161 state->sp = regs->regs[3]; 162 state->ra = regs->regs[1]; 163 state->first = true; 164 get_stack_info(state->sp, state->task, info); 165 166 return true; 167 } 168 } 169 170 state->sp = info->next_sp; 171 172 } while (!get_stack_info(state->sp, state->task, info)); 173 174 return false; 175 } 176 EXPORT_SYMBOL_GPL(unwind_next_frame); 177