1 // SPDX-License-Identifier: GPL-2.0 1 2 /* 3 * Copyright (C) 2022 Loongson Technology Corp 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 31 { 32 int idx, offset; 33 34 if (entry_offset >= EXCCODE_INT_START 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_a 42 case EXCCODE_ALE: 43 return offset == unwind_hint_a 44 case EXCCODE_BP: 45 return offset == unwind_hint_b 46 case EXCCODE_FPE: 47 return offset == unwind_hint_f 48 case EXCCODE_FPDIS: 49 return offset == unwind_hint_f 50 case EXCCODE_LSXDIS: 51 return offset == unwind_hint_l 52 case EXCCODE_LASXDIS: 53 return offset == unwind_hint_l 54 case EXCCODE_BTDIS: 55 return offset == unwind_hint_l 56 case EXCCODE_INE: 57 return offset == unwind_hint_r 58 case EXCCODE_WATCH: 59 return offset == unwind_hint_w 60 default: 61 return false; 62 } 63 } 64 65 static inline bool fix_exception(unsigned long 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_ha 74 return true; 75 } 76 #endif 77 return scan_handlers(pc - eentry); 78 } 79 80 /* 81 * As we meet ftrace_regs_entry, reset first f 82 * tracing. Prologue analysis will stop soon b 83 */ 84 static inline bool fix_ftrace(unsigned long pc 85 { 86 #ifdef CONFIG_DYNAMIC_FTRACE 87 return pc == (unsigned long)ftrace_cal 88 #else 89 return false; 90 #endif 91 } 92 93 static inline bool unwind_state_fixup(struct u 94 { 95 if (!fix_exception(state->pc) && !fix_ 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 sa 107 * st.d yy, sp, offset save ra if fun 108 * [others instructions] 109 */ 110 static bool unwind_by_prologue(struct unwind_s 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->stac 117 union loongarch_instruction *ip, *ip_e 118 119 if (state->sp >= info->end || state->s 120 return false; 121 122 if (state->reset) { 123 regs = (struct pt_regs *)state 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 134 * We need to adjust its value in case 135 */ 136 pc = state->pc - (state->first ? 0 : L 137 if (!kallsyms_lookup_size_offset(pc, & 138 return false; 139 140 ip = (union loongarch_instruction *)(p 141 ip_end = (union loongarch_instruction 142 143 while (ip < ip_end) { 144 if (is_stack_alloc_ins(ip)) { 145 frame_size = (1 << 12) 146 ip++; 147 break; 148 } 149 ip++; 150 } 151 152 /* 153 * Can't find stack alloc action, PC m 154 * first being true is reasonable, oth 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 166 break; 167 } 168 if (is_branch_ins(ip)) 169 break; 170 ip++; 171 } 172 173 /* Can't find save $ra action, PC may 174 if (frame_ra < 0) { 175 if (state->first) { 176 state->sp = state->sp 177 goto first; 178 } 179 return false; 180 } 181 182 state->pc = *(unsigned long *)(state-> 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) || __ 192 } 193 194 static bool next_frame(struct unwind_state *st 195 { 196 unsigned long pc; 197 struct pt_regs *regs; 198 struct stack_info *info = &state->stac 199 200 if (unwind_done(state)) 201 return false; 202 203 do { 204 if (unwind_by_prologue(state)) 205 state->pc = unwind_gra 206 return true; 207 } 208 209 if (info->type == STACK_TYPE_I 210 regs = (struct pt_regs 211 pc = regs->csr_era; 212 213 if (user_mode(regs) || 214 goto out; 215 216 state->first = true; 217 state->pc = pc; 218 state->ra = regs->regs 219 state->sp = regs->regs 220 get_stack_info(state-> 221 222 return true; 223 } 224 225 state->sp = info->next_sp; 226 227 } while (!get_stack_info(state->sp, st 228 229 out: 230 state->stack_info.type = STACK_TYPE_UN 231 return false; 232 } 233 234 unsigned long unwind_get_return_address(struct 235 { 236 return __unwind_get_return_address(sta 237 } 238 EXPORT_SYMBOL_GPL(unwind_get_return_address); 239 240 void unwind_start(struct unwind_state *state, 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 a 249 * relative symbol. Thus, prologue ana 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(stat 256 } 257 } 258 EXPORT_SYMBOL_GPL(unwind_start); 259 260 bool unwind_next_frame(struct unwind_state *st 261 { 262 return state->type == UNWINDER_PROLOGU 263 next_frame(state) : de 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.