1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2022 Loongson Technology Corporation Limited 4 */ 5 #include <linux/cpumask.h> 6 #include <linux/ftrace.h> 7 #include <linux/kallsyms.h> 8 9 #include <asm/inst.h> 10 #include <asm/loongson.h> 11 #include <asm/ptrace.h> 12 #include <asm/setup.h> 13 #include <asm/unwind.h> 14 15 extern const int unwind_hint_ade; 16 extern const int unwind_hint_ale; 17 extern const int unwind_hint_bp; 18 extern const int unwind_hint_fpe; 19 extern const int unwind_hint_fpu; 20 extern const int unwind_hint_lsx; 21 extern const int unwind_hint_lasx; 22 extern const int unwind_hint_lbt; 23 extern const int unwind_hint_ri; 24 extern const int unwind_hint_watch; 25 extern unsigned long eentry; 26 #ifdef CONFIG_NUMA 27 extern unsigned long pcpu_handlers[NR_CPUS]; 28 #endif 29 30 static inline bool scan_handlers(unsigned long entry_offset) 31 { 32 int idx, offset; 33 34 if (entry_offset >= EXCCODE_INT_START * VECSIZE) 35 return false; 36 37 idx = entry_offset / VECSIZE; 38 offset = entry_offset % VECSIZE; 39 switch (idx) { 40 case EXCCODE_ADE: 41 return offset == unwind_hint_ade; 42 case EXCCODE_ALE: 43 return offset == unwind_hint_ale; 44 case EXCCODE_BP: 45 return offset == unwind_hint_bp; 46 case EXCCODE_FPE: 47 return offset == unwind_hint_fpe; 48 case EXCCODE_FPDIS: 49 return offset == unwind_hint_fpu; 50 case EXCCODE_LSXDIS: 51 return offset == unwind_hint_lsx; 52 case EXCCODE_LASXDIS: 53 return offset == unwind_hint_lasx; 54 case EXCCODE_BTDIS: 55 return offset == unwind_hint_lbt; 56 case EXCCODE_INE: 57 return offset == unwind_hint_ri; 58 case EXCCODE_WATCH: 59 return offset == unwind_hint_watch; 60 default: 61 return false; 62 } 63 } 64 65 static inline bool fix_exception(unsigned long pc) 66 { 67 #ifdef CONFIG_NUMA 68 int cpu; 69 70 for_each_possible_cpu(cpu) { 71 if (!pcpu_handlers[cpu]) 72 continue; 73 if (scan_handlers(pc - pcpu_handlers[cpu])) 74 return true; 75 } 76 #endif 77 return scan_handlers(pc - eentry); 78 } 79 80 /* 81 * As we meet ftrace_regs_entry, reset first flag like first doing 82 * tracing. Prologue analysis will stop soon because PC is at entry. 83 */ 84 static inline bool fix_ftrace(unsigned long pc) 85 { 86 #ifdef CONFIG_DYNAMIC_FTRACE 87 return pc == (unsigned long)ftrace_call + LOONGARCH_INSN_SIZE; 88 #else 89 return false; 90 #endif 91 } 92 93 static inline bool unwind_state_fixup(struct unwind_state *state) 94 { 95 if (!fix_exception(state->pc) && !fix_ftrace(state->pc)) 96 return false; 97 98 state->reset = true; 99 return true; 100 } 101 102 /* 103 * LoongArch function prologue is like follows, 104 * [instructions not use stack var] 105 * addi.d sp, sp, -imm 106 * st.d xx, sp, offset <- save callee saved regs and 107 * st.d yy, sp, offset save ra if function is nest. 108 * [others instructions] 109 */ 110 static bool unwind_by_prologue(struct unwind_state *state) 111 { 112 long frame_ra = -1; 113 unsigned long frame_size = 0; 114 unsigned long size, offset, pc; 115 struct pt_regs *regs; 116 struct stack_info *info = &state->stack_info; 117 union loongarch_instruction *ip, *ip_end; 118 119 if (state->sp >= info->end || state->sp < info->begin) 120 return false; 121 122 if (state->reset) { 123 regs = (struct pt_regs *)state->sp; 124 state->first = true; 125 state->reset = false; 126 state->pc = regs->csr_era; 127 state->ra = regs->regs[1]; 128 state->sp = regs->regs[3]; 129 return true; 130 } 131 132 /* 133 * When first is not set, the PC is a return address in the previous frame. 134 * We need to adjust its value in case overflow to the next symbol. 135 */ 136 pc = state->pc - (state->first ? 0 : LOONGARCH_INSN_SIZE); 137 if (!kallsyms_lookup_size_offset(pc, &size, &offset)) 138 return false; 139 140 ip = (union loongarch_instruction *)(pc - offset); 141 ip_end = (union loongarch_instruction *)pc; 142 143 while (ip < ip_end) { 144 if (is_stack_alloc_ins(ip)) { 145 frame_size = (1 << 12) - ip->reg2i12_format.immediate; 146 ip++; 147 break; 148 } 149 ip++; 150 } 151 152 /* 153 * Can't find stack alloc action, PC may be in a leaf function. Only the 154 * first being true is reasonable, otherwise indicate analysis is broken. 155 */ 156 if (!frame_size) { 157 if (state->first) 158 goto first; 159 160 return false; 161 } 162 163 while (ip < ip_end) { 164 if (is_ra_save_ins(ip)) { 165 frame_ra = ip->reg2i12_format.immediate; 166 break; 167 } 168 if (is_branch_ins(ip)) 169 break; 170 ip++; 171 } 172 173 /* Can't find save $ra action, PC may be in a leaf function, too. */ 174 if (frame_ra < 0) { 175 if (state->first) { 176 state->sp = state->sp + frame_size; 177 goto first; 178 } 179 return false; 180 } 181 182 state->pc = *(unsigned long *)(state->sp + frame_ra); 183 state->sp = state->sp + frame_size; 184 goto out; 185 186 first: 187 state->pc = state->ra; 188 189 out: 190 state->first = false; 191 return unwind_state_fixup(state) || __kernel_text_address(state->pc); 192 } 193 194 static bool next_frame(struct unwind_state *state) 195 { 196 unsigned long pc; 197 struct pt_regs *regs; 198 struct stack_info *info = &state->stack_info; 199 200 if (unwind_done(state)) 201 return false; 202 203 do { 204 if (unwind_by_prologue(state)) { 205 state->pc = unwind_graph_addr(state, state->pc, state->sp); 206 return true; 207 } 208 209 if (info->type == STACK_TYPE_IRQ && info->end == state->sp) { 210 regs = (struct pt_regs *)info->next_sp; 211 pc = regs->csr_era; 212 213 if (user_mode(regs) || !__kernel_text_address(pc)) 214 goto out; 215 216 state->first = true; 217 state->pc = pc; 218 state->ra = regs->regs[1]; 219 state->sp = regs->regs[3]; 220 get_stack_info(state->sp, state->task, info); 221 222 return true; 223 } 224 225 state->sp = info->next_sp; 226 227 } while (!get_stack_info(state->sp, state->task, info)); 228 229 out: 230 state->stack_info.type = STACK_TYPE_UNKNOWN; 231 return false; 232 } 233 234 unsigned long unwind_get_return_address(struct unwind_state *state) 235 { 236 return __unwind_get_return_address(state); 237 } 238 EXPORT_SYMBOL_GPL(unwind_get_return_address); 239 240 void unwind_start(struct unwind_state *state, struct task_struct *task, 241 struct pt_regs *regs) 242 { 243 __unwind_start(state, task, regs); 244 state->type = UNWINDER_PROLOGUE; 245 state->first = true; 246 247 /* 248 * The current PC is not kernel text address, we cannot find its 249 * relative symbol. Thus, prologue analysis will be broken. Luckily, 250 * we can use the default_next_frame(). 251 */ 252 if (!__kernel_text_address(state->pc)) { 253 state->type = UNWINDER_GUESS; 254 if (!unwind_done(state)) 255 unwind_next_frame(state); 256 } 257 } 258 EXPORT_SYMBOL_GPL(unwind_start); 259 260 bool unwind_next_frame(struct unwind_state *state) 261 { 262 return state->type == UNWINDER_PROLOGUE ? 263 next_frame(state) : default_next_frame(state); 264 } 265 EXPORT_SYMBOL_GPL(unwind_next_frame); 266
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.