xref: /linux/arch/loongarch/kernel/unwind_prologue.c (revision be239684b18e1cdcafcf8c7face4a2f562c745ad)
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