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