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(entry_offset: pc - pcpu_handlers[cpu])) |
74 | return true; |
75 | } |
76 | #endif |
77 | return scan_handlers(entry_offset: 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(pc: state->pc) && !fix_ftrace(pc: 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(addr: pc, symbolsize: &size, offset: &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(addr: 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(addr: 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(addr: 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 | |